文章的开始
在学习对象这部分的内容时候我更加明显的感觉到原生js的重要性。所以我又把js里面的this知识点给复习了一下。 关于this,这个关键字在很多语言中都会出现,但是this的指向很多初学者模棱两可。在JavaScript中,this一般的指向在函数定义的时候是确定不了的,至于在函数执行的时候才能确定,实际上this指向的是最终调用自己的对象。
this指向的情况,取决于函数调用的方式
1.函数预编译过程中,this指向window
函数在执行之前会有一个预编译的过程,如果对预编译不是很清楚可以看看我的上一篇文章。
例题1
function test(c) {
var a =13;
function b() {}
}
test(1);
/*预编译生成的AO如下
AO{
arguments : [1],
this: window,//指向window
c : 1,
a : undefined
b : function(){}
} */
如果test是构造函数。new test() 的时候会在构造函数里面产生一个this对象然后返回。如果不new的话this仍然指向window
//构造函数中产生的this对象
var this = Object.creat(test.prototype);
==
var this = {
__proto__ : test.prototype;
}
紧接着来看下一个例子
例题2
var foo = 123;
function Print() {
this.foo = 234;
console.log(foo);
console.log(this.foo);
}
new Print()
console.log(foo)里面指的是全局window中的foo=123,console.log(this.foo)指的是Print里的foo=234
2.全局作用域里面,this指向window
在全局作用域中,this始终是指向window的。
3.call/apply
call/apply 可以改变this的指向,借用别人的函数来实现自己的功能。
例题3
function Person(name, age, sex) {
this.name = name;
this.sex = sex;
this.age = age;
}
function Student(name, sex, age, grade, tel) {
Person.call(this, name, age, sex);
this.grade = grade;
this.tel = tel;
}
var student = new Student(name, sex, age, grade, tel);
Person.call()函数中的this指的是当前Student中的var this={},然后传递给了person,也就是说person里的this是student
this.name == student.name
this.sex ==student.sex
借用person函数实现了student函数的功能。那么var this={name:,sex:}
4.通过对象名.函数名调用,this指向该对象
例题4
var name = "qqq";
var obj1 = {
name: "www",
say: function () {
console.log("this.name");
}
}
var obj2 = {
name :"eee",
say: function (fun) {
fun();
}
}
obj2.say(obj1.say)
这是一个有趣的例子,按道理讲obj2.say()中的this指向obj2,但是打印的结果却不是eee,而是全局里面的qqq。这是为什么呢。原因分为以下四步
1.obj2.say方法执行的时候,把obj1.say方法作为参数传递并执行,
2.但是执行的时候并不是this.fun(),而是fun(),
3.所以fun中的this并不是指向obj2,没有对象调用的时候走预编译环节,指向全局。
4.所以最后结果为qqq。
例题5
var foo = 123;
function test() {
this.foo = 234;
console.log(foo);
}
test();
new test();
这道题执行test和new test的结果是不一样的,虽然两次都是打印全局的foo但是结果却不同。
首先看执行test的时候,this指向window,this.foo = 123 修改的是全局window中的foo,所以打印的也是全局的foo,值为234.
然后看执行new test,new test()在test中会var this= {__ proto __ : test.prototype;},也就是此时的this指向test,console.log(foo)!=console.log(this.foo),所以打印的foo是全局中的123。
关于this更新了一道笔试题
(2019.12.13更新)
最近看到一个考this绑定的题目,this有四种绑定。默认,显示,隐式,new绑定。我们先看看题目。
var number=2;//4
var obj={
number:4,//
/*立即执行函数自调*/
fn1:(function(){
var number;//3
this.number*=2;//
number=number*2;//
number=3;//
return function(){
var num=this.number; //
this.number*=2; //
console.log(num); //
number*=3; //
console.log(number); //
}
})()
}
var fn1=obj.fn1;
console.log(number);// 4
fn1();//4,9
obj.fn1();// 8,9.应该是4 27
console.log(window.number);// 8
console.log(obj.number);// 16。应该是8
首先会进行预编译环节,有两个全局变量number,obj。我们约定全局的number为window.number。在编译环节,我们约定obj对象中的number为obj.number先碰到立即执行符号()然后才会立即执行obj中的方法,fn1是一个立即执行函数,虽然函数在obj对象中,但是这里this指向全局,this的四种绑定可以参考这篇文章
number:4,//
/*立即执行函数自调*/
fn1:(function (){
var number;//3
this.number*=2;//
number=number*2;//
number=3;//
return function(){
var num=this.number; //
this.number*=2; //
console.log(num); //
number*=3; //
console.log(number); //
}
})()
}
===等价于以下代码===
var obj={
number:4,//
/*立即执行函数自调*/
fn1:function(){
var number;//3
this.number*=2;//
number=number*2;//
number=3;//
return function(){
var num=this.number; //
this.number*=2; //
console.log(num); //
number*=3; //
console.log(number); //
}
然后这里执行匿名函数。注意执行的是匿名函数,不是this.匿名函数。
}
}
this这个时候指向全局,所以this.number就是4。其中fun1这个函数里面也有一个number,为我们约定为fn1.number。fn1.number最开始只是变量声明没有赋值,所以开始是undefined,最后的结果是3。然后立即执行函数又返回了一个匿名函数。我们在全局变量用fn1得到了立即执行函数返回的结果。 上面是对立即函数的分析,此时window.number==4;obj.number===4;fn1.number===3,下面我们一步步来分析打印输出的结果
1.对于console.log(number);
这个时候的number指的是全局中的number,所以结果是4
2.对于fn1();
这个时候fn1是立即执行函数返给的一个函数,同时形成了闭包。这个时候this是指向window的,然后num接受了window上的number值,通过前面的分析知道wimdow.number是4,所以第一个打印的结果是4,同时改变全局的变量numebr变成了8。
这里的number是一个没有声明的变量,自己内部没有就沿着作用域链去寻找,又因为是闭包能够拿到立即执行函数中声明的number,所以number==fn1.number==3,最后打印的结果是9。执行完后释放图自己的AO,GO没有释放。玲珑有一篇专门讲作用域链的文章嘻嘻嘻。 此时obj.number==4;window.nmber==8;
3.对于obj.fn1()
对于这一问,此时的this是指向obj的。指的注意的是fn1就是被返回的匿名函数,会直接跳转到return语句部分执行,并不会再重新执行立即执行函数。 num=this.number==obj.number;所以第一个结果是4,并且让他的obj的number变成了8。
此时的number也是因为闭包,原理同第二问一样。最后的结果是27。所以结果分别是4,27。 window.number由2可知结果是8,obj.number也是8.
4.对于后面的
后面分别打印全局和obj中的number,前面分析了这里就不再复述。
简化原型访问Super引用
ES6中引入了Super的引用特性,可以便捷的访问对象原型。这里重点解释《深入理解es6》这本书中将这个知识点的理解。如果对这部分不理解的朋友可以仔细看看。
如果想重写对象实例的方法,又需要调用与它同名的原型方法在es5中的具体实现
let person = {
getGreeting() {
return "hello";
}
}
let dog = {
getGreeting() {
return "woof";
}
};
let friend = {
getGreeting() {
return Object.getPrototypeOf(this).getGreeting.call(this)+",hi";
}
};
//将原型设置为person
Object.setPrototypeOf(friend,person);
console.log(friend.getGreeting()); //hello,hi
console.log(Object.getPrototypeOf() === pesrson)//true
1.定义了一个person对象,一个dog对象,两个对象中都有一个getGreeting的简写方法。
2.friend对象中,首先this指向的是friend对象,因为friend.getGreeting()通过对象名.函数名调用的this指向该对象。所以Object.getPrototypeOf(this)返回了friend的原型person。
3.Object.getPrototypeOf(this).getGreeting.call(this)+",hi" == person.getGreeting.call(this)+",hi";
4.person.getGreeting.call(this)中的this同之前一样指向friend,也就是将person对象中的getGreeting带到了friend中的getGreeting方法中,满足了重写对象实例方法以及在此方法中调用了与之同名的原型方法。
在es6中用了super,super.getGreeting() + 'hi!';Super引用相当于指向对象原型的指针
文章的结束
写到这里就结束了,主要讲的还是es5中的this应用和es6新增的方法。学习一定要弄清楚原理以及某一个知识点的用途和意义。