# Javascript两链一包还有this
# 原型链
访问一个属性时,先在基本属性上找,如果没有,再沿着 _proto_
这条链往上找,这就是原型链。原型链的最后是 null
。
构造函数可以设置 prototype
实现继承就是这个原理。
在创建对象时默认会把 Object
的原型指定到当前原型链上。
// 创建对象
let obj = {name: 'abc'};
obj.hasOwnProperty('name') // hasOwnProperty是从Object原型上的方法
// 原型链的顺序为 obj -> Object -> null
// 如果不希望obj原型链上有Object
let obj = Object.create(null); // 创建纯{}
构造函数生成实例对象
function Person(name) {
this.name = name;
}
Person.prototype.foo = 'foo';
let abc = new Person('abc'); // 原型链 abc -> Person -> Object -> null
abc.name; // 'abc' 直接从实例对象上找到
abc.foo; // 'foo' 从Person找到
abc.hasOwnProperty; // 从Object找到
function Programmer(job) {
this.job = job;
}
Programmer.prototype = new Person('bar');
let h5er = new Programmer('h5'); // // 原型链 h5er -> Programmer -> Person -> Object -> null
# 作用域链
当在函数中访问变量时会搜索作用域链查找,一层层就会形成作用域链。
let name = 'window';
function getName() {
console.log(name); // 这里是找不到name的,这里由于下面的定义会形成一个TDZ(暂时性死区) 如果用 var 将会输出 undefined 下面没有定义才会输出 window
let name = 'getname';
console.log(name); // getname
}
getName(); // getname 作用域链 getName -> window/global
作用域不是根据 运行位置 确定的,是根据 定义位置 确定。
let name = 'window';
function getName() {
console.log(name);
}
function fn() {
let name = 'fn';
getName();
}
fn(); // window
# this
我们知道 this
对象是在运行时基于函数的执行环境绑定的:在全局函数中,this
等于 window
, 而当函数作为某个对象的方法调用时,this
等于那个对象。
console.log(this); // window
var name = 'the window';
function getName() {
console.log(this.name); // 这里并没有指定this
}
var o = {
name: 'abc'
}
function fn() {
let name = 'fn';
getName();
}
getName() // the window 执行的时候才会指定 this 现在 this指向了window
o.getName = getName;
o.getName(); // abc 当函数作为某个对象的方法调用时,`this` 等于那个对象
let a = o.getName.bind(this); // this被指定到了window 一经绑定就不能改变了
a(); // the window
a.bind(o); // 这里已经不会改变指向了 所以还是输出 the window
o.getName.call(this); // the window this被指定到了window
o.getName.apply(this); // the window this被指定到了window
fn(); // the window 这里不输出 fn 是因为最终运行的环境还是window this最终的指向不是由定义的地方决定 而是由运行时基于函数的执行环境绑定的
# 闭包
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常用方式是在一个函数内部创建另一个函数。简单说就是函数对内有引用,对外有返回。
function calc() {
let i = 0;
function changeVal(val) {
i+=val;
}
return {
add: function() {
changeVal(1);
return this; // 支持链式调用
},
minus: function() {
changeVal(-1);
return this; // 支持链式调用
},
value: function() {
return i;
}
}
}
let instance1 = calc();
instance1.add().add().minus().value(); // 1
let instance2 = calc(); // 第二个还是从0开始
instance2.add().add().value(); // 2
instance1 = null; // instance1 结束引用 触发垃圾回收
instance2 = null; // instance2 结束引用 触发垃圾回收
// 不结束引用 i会一直存在
# 闭包的作用
- 私有变量:从上面可以看出来
i
只对外面暴漏一个查看方法,无法直接修改。如果不暴露value
查看也不能查看。只能通过暴漏的接口对它进行操作。 - 不清除外部的引用,内部对外的变量会一直都存在。
← 原生实现下Array的方法 防抖和节流 →