你不知道的JS读书笔记2—this对象

来源:互联网 发布:淘宝会员等级表 编辑:程序博客网 时间:2024/05/22 08:11

本文是在《你不知道的JS》上阅读了其他的资料之后整理的。

this是很重要的一个对象,它不指向它本身不指向它的作用域,它指向函数调用所处的位置。需要分析函数的调用栈,调用位置就是调用栈中当前函数的上一个函数。

比如如下代码:

function foo(){    console.log(this.a);}var a = 2;var obj = {    a : 1,    foo: foo}obj.foo();   // 1 调用栈为 obj->foo,所以this指向objvar bar = obj.foo;foo();  // 2 foo在全局作用域下被调用,所以this指向全局作用域bar();  // 2  bar其实就是foo,所以调用栈就是foo,this指向全局作用域

首先函数的调用方式大致分为普通函数调用,作为对象方法调用,call/apply调用,new构造器调用。我们分别来说这几种情况下this的指向问题,以及几种调用情况共存时候的优先级。

new绑定

new一个对象的具体过程如下:
1. 新创建一个对象;
2. 建立这个对象与原型链的联系;
3. 将这个新对象绑定this;
4. 如果没有返回的对象,就自动返回this。

所以用new创建出来的函数,this都指向这个新建的实例。

function foo(){     this.a = 2;     this.hello = function(){         console.log(this.a);     } }     var bar = new foo(); bar.hello();  // 2

正常情况下,foo函数中的this指向全局作用域,但是new的操作强行将新创建的bar对象绑定this。new绑定的优先级是最高的。

显示绑定

直接用apply或者call的方式强行指定this对象。

function foo(){    console.log(this.a);}var a = 2;var obj = {    a : 1};var obj2 = {    a : 10};foo.call( obj );  // 1 强制将this指向obj对象foo.call( obj2 );  // 10 强制将this指向obj2对象foo();   //2

这里仍然有个问题,foo的this可以被改变的。下面这种硬绑定的方法可以在一般情况下,foo的this固定。

function foo(){    console.log(this.a);}var obj = {    a : 1};var obj1 = {    a : 10};function bar(){   return  foo.apply(obj, arguments) ;}bar();  // 1bar.apply(obj1);  // 1  this仍然指向obj,未改变

ES5中封装了bind函数,去实现上述的功能。简单的实现版本如下

Function.prototype.bind = function(context){     var self = this;     return function(){         return self.apply(context, arguments);     } }

另外,在原生JS中,有些接口本来就能做this的设置,比如forEach, map, every, some, map, filter等。显示绑定的优先级仅次于new绑定。

隐式绑定

当函数作为对象的方法被调用时,this指向这个调用的对象。没什么特别之处,按照调用栈来看就好。优先级次之。需要注意的是,当多个属性嵌套时,使用最近原则。

function foo(){    console.log(this.a);}var a = 2;var obj = {    a : 1,    foo: foo,    obj2: obj2}var obj2 = {    a : 2,    foo: foo}obj.obj2.foo();   // 2 调用栈为 obj->obj2->foo,所以this指向obj2

默认绑定

就是普通函数的调用。需要说明的是,在非严格模式下,this指向全局对象window,而严格模式下,this指向undefined。优先级最低。如果绑定null或者undefined,应用的是默认绑定规则。

箭头函数

我们先来看个例子。

var point = {    x : 0,    y : 0,    moveTo : function(x, y) {        // 内部函数       var moveX = function(x) {            this.x = x;//this 绑定到了哪里?       };       // 内部函数       var moveY = function(y) {           this.y = y;//this 绑定到了哪里?       };       moveX(x);       moveY(y);    }};point.moveTo(1, 1);point.x; //==>0point.y; //==>0x; //==>1y; //==>1

这个例子看起来不是上面四个规则中的任何一个。 point.moveTo是以对象属性方法的方式调用的,当moveTo函数内部this应该指向point对象。当它并没有直接在moveTo函数内部直接调用this,而是在内部又定义了两个函数变量moveX,moveY,将两个匿名函数赋值给它们。那匿名函数内部的this应该指向point.moveTo? ………..(此时省略N个字,具体的原因谁能说说) 总之,this的绑定对象莫名其妙地绑定了全局变量。

为了避免这种情况的发生,一般经典的做法为:

var point = {    x : 0,    y : 0,    moveTo : function(x, y) {       var that = this;       // 内部函数       var moveX = function(x) {        that.x = x;       };       // 内部函数       var moveY = function(y) {        that.y = y;       }       moveX(x);       moveY(y);    }};point.moveTo(1, 1);point.x; //==>1point.y; //==>1

另外,ES6出来了一个箭头函数,不仅是形式上的简化,它还严格符合词法作用域。可以替换平时写代码时加的var that = this

var point = {    x : 0,    y : 0,    moveTo : function(x, y) {          // 内部函数         var moveX =  x => {this.x = x;};         // 内部函数         var moveY = y => {this.y = y;};         moveX(x);         moveY(y);     }};point.moveTo(1, 1);point.x; //==>1point.y; //==>1

参考资料:
https://github.com/getify/You-Dont-Know-JS 你不知道的JS
http://www.ruanyifeng.com/blog/2010/04/using_this_keyword_in_javascript.html JavaScript的this用法
https://www.ibm.com/developerworks/cn/web/1207_wangqf_jsthis/ 深入浅出 JavaScript 中的 this
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this MDN this

0 0