一、为什么要用this

能够隐式的传递对象引用,更加简洁。

二、this指向规则和词法作用域

词法作用域:作用域由函数书写代码时决定(定义时上下文)

this指向规则:不指向函数词法作用域,不指向函数本身,由函数调用时的位置决定(运行时上下文,类似于动态作用域)

三、this指向规则

this的指向规则如下四条,优先级为:new +构造函数 > 显式绑定 > 隐式绑定 > 默认

1.默认(独立调用)

独立函数调用情况下,this指向全局变量或undefined。

(1)函数在非严格模式下,this指向全局变量

(2)函数在严格模式下(use strict),this 指向undefined

2.隐式绑定

(1)调用位置有上下文对象的,this绑定在上下文对象上

(2)调用位置有多个链式上下文对象的,this绑定在就近的对象上

注意:隐式绑定可能会出现this的隐式丢失,原因如下:

var f = function () {
  console.log(this.x);
}

var x = 1;
var obj = {
  f: f,
  x: 2,
};

// 单独执行
f() // 1

// obj 环境执行
obj.f() // 2

javascript的this指向 js中this的指向问题_this

javascript的this指向 js中this的指向问题_作用域_02

 

(上例和图片来自http://www.ruanyifeng.com/blog/2018/06/javascript-this.html

由以上例子能够看出,当定义一个对象,并在对象中定义函数的过程是先开辟一个内存存储函数,然后将地址传递给对象的foo。当调用foo()时,是直接调用全局中的foo函数;当调用obj.foo()时是访问obj.foo存储的地址,此时this指向obj。

若再次定义var a = obj.foo;即将地址传给了全局的a,此时调用a()相当于直接调用全局中的函数,此时this指向全局变量(非严格模式下)造成this的隐式丢失

在回调函数和参数传递(隐式赋值)时,容易发生this的隐式丢失。

隐式丢失的解决办法:

(1)var self = this

(2)硬绑定(bind)

(3)箭头函数(ES6)

3.显式绑定

显式的绑定this指向可以采用call()、apply()和硬绑定bind()

call()和apply()的作用完全一样,只是参数传递方式不一样

(1)函数.call ( this新指向,数据1,... ,数据n)

将函数中的this指向新指向,并向该函数传递数据1-n

(2)函数.apply(this新指向,数组或类数组) 其中数组或类数组为:[数据1,... ,数据n]

将函数中的this指向新指向,并向该函数传递数组 [数据1,...,数据n]

call()和apply() 的用法:

  • 劫持别人的方法
var foo = {
  name:"mingming",
  logName:function(){
    console.log(this.name);
  }
}
var bar={
  name:"xiaowang"
};
foo.logName.call(bar);//xiaowang
  • 实现继承
function Animal(name){   
  this.name = name;   
  this.showName = function(){   
    console.log(this.name);   
  }   
}   
 
function Cat(name){  
  Animal.call(this, name);  
}   
 
var cat = new Cat("Black Cat");   
cat.showName(); //Black Cat
  • 强制改变this指向
window.id="window";
document.querySelector('#test').onclick = function(){
  console.log(this.id);//test
  var fun = function(){
    console.log(this.id);
  }
  fun();//window
  fun.call(this);//test
}

(3)硬绑定bind(未完)

bind()是ES5新增(IE6,7,8不支持)

var  新函数 = 旧函数.bind(this新指向,数据1,... ,数据n)

bind() 作用和前两个相同,即改变this指向

call()和apply()在改变指向以后立即执行改变后的函数,而bind()却返回一个改变后的新函数。

注意:call(),apply()和bind() 在非严格模式下,第一个参数为null或者undefined时this会自动替换为指向全局对象

4.new+构造函数

在传统面向对象语言中,构造函数是类的特殊函数

在JavaScript中,构造函数只是能够使用new操作符调用的普通函数,他不属于某个类,也不能实例一个类

当使用new 来调用构造函数时,自动执行一下操作:

(1)创建一个新的对象

(2)对新对象执行 [[原型]] 连接

(3)把新对象绑定到函数的this

(4)若函数没有返回其他对象,则函数自动返回这个新对象

四、ES6改进(箭头函数)

箭头函数的this不按照以上四条规则,而是按照词法作用域由外部作用域决定this指向

箭头函数能够通过call、apply、bind 改变this指向,但只能改变一次

参考资料:

http://www.ruanyifeng.com/blog/2018/06/javascript-this.html

 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind