【记录一下这些概念的一些容易混淆的细节,不包含基本的概念讲解,只包含容易搞错的细节】
- 一个函数只要是直接调用的,无论是在哪里直接调用,它的this都是window,因此,哪怕一个函数是嵌套在一个对象的函数的函数内返回的对象的函数内,无论套路多少,只要是直接调用的方式,那this就是window,因此访问不到其他任何不在window对象和作用域链内的属性,如下面的代码。
function father(){
this.name='a';
return {x:2,
age:30,
y:function(){
this.xyz=20;
function a(){
this.aaa=2;
console.log(xyz)
console.log(this.xyz)//这两种都访问不到xyz
console.log(this.a.xyz)//这样能够访问到xyz,
//------------------------------------------------
console.log(a)//这里返回的是a这个函数,不是对象a
console.log(window.a)
console.log(this.a)//这两种才是访问到对象a
//因为var a使得a在window内
}
a();
}
};
}
var a = new fathor();
a.y()
a是father返回的对象,对象a调用了函数y,因此y内的this是a,this.xyz这一行把属性xyz添加进了a对象里,而y内的函数a是直接调用的,因此函数a的this是window,this.aaa是把aaa添加给了window对象,一个函数能访问到的属性只有window对象,this,还有作用域链上的属性,而访问不到其他对象内的属性,因此,由于this.xyz是把一个属性添加进了一个对象a,而对象a又不是调用函数a的对象,所以是无法直接访问到xyz的。
-
上面的举例,对象a和函数a是刻意重名的,注意看分割线下面的三行打印,直接打印a的话,是函数a,其实就是顺着作用域链一层一层往外寻找,如果找不到,最后再找到最外层的window。【这里补充一句,作用域链最外层是全局环境的变量对象,这个环境跟window对象内的成员几乎是一样的,但是呢在es6以后这样说不准确了,因为这种一个对象实例,跟执行代码时创建的变量对象有耦合关系,是不太合理的,因此,es6之后加入的变量声明方式,例如class,let,const,这些命令声明的变量是在作用域链顶端的,但不在window对象内!】
-
还是上面的举例,要特别强调一个点,this.xyz,在找xyz的过程中会顺着原型链找不假,但找不到原型链上的对象内的属性啊!如果有一个属性是对象,对象内有xyz,也是不会通过原型链找到的。
-
再强调一个点,作用域链和原型链还是有区别的,作用域链上的通过属性名就可以直接访问到,而原型链必须通过obj.属性名才能访问到,因此,如果一个函数被一个原型链很长的对象调用了,那么必须要通过this来访问原型链上的属性,而通过属性名直接访问到的是作用域链上的。
-
接第4条,因此,如果一个属性不在作用域链,而是在原型链上,那么直接通过属性名是访问不到的。
-
任何一个对象,它的原型链终点都是obj对象!就是Object()这个构造器new出来的对象,然后再往前访问__proto__就会得到null!而不是undefined!正常来说访问一个不存在的属性,应该是undefined,因此__proto__的尽头不是未定义,而是null哦。
-
window这个对象除非强行添加,否则是不在原型链里的,它是作用域链的尽头(补充:这个说法不准确,作用域链尽头是全局执行环境的变量对象,虽然es6之前全局环境的变量也是window对象的属性,但es6之后逐渐脱钩了,就不能说window对象是作用域链的尽头了),但不是原型链的。
-
综上,通过属性名访问一个变量,会沿着作用域链一直找到window对象(因为window也是个对象,所以也会沿着window的原型链找,一直找到obj对象),找不到的话,会报错“Uncaught ReferenceError: 变量 is not defined” ,(报错底下的代码就不执行了啊),如果通过对象.属性来访问,是沿着该对象的原型链寻找一直找到obj对象,如果属性不存在,不会报错,(不报错代码还能执行的!),但打印该属性一样是返回undefined。
-
因此,如果一个属性在this内,但不在window内,直接访问属性名是访问不到的,但如果该函数的this就是window,那么直接访问名,跟通过this访问属性名,效果就是一样了。
-
this是指的调用该函数的对象,如果一个函数是直接调用的,无论这个函数在哪个犄角旮旯里直接调用,this都是window,如果是由某个对象调用,或用call,apply,bind修改了this,则this就指向目标对象。
-
一个常见的说法是this是执行上下文,但是呢,上面分析过了,原型链跟作用域链还是有点区别的,所以虽然this是执行上下文,或者说函数的执行环境,但严格来讲,执行环境应该是作用域链+this的原型链,这俩加起来才是该函数能访问到的所有内容,不说window对象是因为window对象内的属性全部都在作用域链尽头了。
以上这些道理讲起来很简单,但面对结构比较复杂的代码时,还是容易懵,所以最好动手敲敲代码。