默认:指向window;隐式:指向调用方法的对象;显式:call, apply, bind;new一个对象的过程;箭头函数
-
this绑定
-
默认绑定与隐式绑定
-
默认:this指向window
-
隐式:this指向调用方法的对象
-
例子
function foo() { console.log(this.bar); } var bar = "bar1"; var o2 = {bar: "bar2", foo: foo}; var o3 = {bar: "bar3", foo: foo}; foo(); // "bar1" – 默认绑定 o2.foo(); // "bar2" – 隐式绑定 o3.foo(); // "bar3" – 隐式绑定foo()这种调用方法,就是默认绑定。如果在非严格模式下,this就是全局对象,浏览器当中就是window。而如果在严格模式(use strict)下,this就会是undefined。
之所以这是默认绑定,因为foo的调用不属于任何人,前面没有任何限定条件。这是最简单的绑定。
o2.foo()和o3.foo()这两种调用方法,都是隐式绑定。Foo是作为o2和o3的方法而调用的,那么谁调用foo,this就指向谁。在上面的例子中,o2.foo()中的this指向o2,因此this.bar就是o2当中的bar: “bar2”;同理,o3.foo()打印出来的就是o3中的”bar3”。
-
例子
function a() { function b() { console.log(this) function c() { "use strict"; console.log(this) } c() } b() } a() // window // undefined // 默认绑定 -
例子
var name = 1 function special() { console.log(this.name) } var girl = { name: 2, detial: function() { console.log(this.name) }, woman: { name: 3, detail: function() { console.log(this.name) } }, special } girl.detail() girl.woman.detail() girl.special() // 2 // 隐式 // 3 // 隐式 // 2 // 隐式,只是赋值给special -
例子
var name = 1 function a() { var name = 2 console.log(this.name) } function d(i) { return i() } var b = { name: 3, detail: function(){ console.log(this.name) }, bibi: function() { // 闭包 return function() { console.log(this.name) } } } var c = b.detail b.a = a var e = b.bibi() a() // 1 默认绑定,所以this指向window c() // 1 默认绑定 b.a() // 3 隐式绑定,指向b d(b.detail) // 1 e() // 1
-
-
显式绑定(硬绑定)
如果foo是通过call、apply或者bind调用的,那么这种调用就是显式绑定。这种绑定中,this的指向就是这三个函数中传递的第一个参数。
-
call的用法:A.call(B,x,y)
- 改变函数A的this指向,使之指向B
- 把A函数放到B中运行(指向B),x和y是A函数的参数
-
apply:传参数是以数组的形式
-
bind:和call一样,但是返回的是函数,所以要()
-
例子
function foo() { console.log(this.bar); } var bar = "bar1"; var obj = {bar: "bar2"}; foo(); // "bar1" 默认绑定 foo.call(obj); // "bar2" 显式绑定,使用obj作为"this"
-
-
关键字new绑定(构造函数绑定)
如果把new这个关键字放在一个函数调用的前面,JS编译器会做这四件事情:
-
创建一个新的空的对象
-
this指向这个新对象,继承该函数的原型
-
属性和方法加到this对象中
-
如果这个函数不返回任何东西,那么就会默认return this
-
例子
function foo() { this.baz = "baz"; console.log(this.bar + " " + baz); } var bar = "bar"; var baz = new foo(); // undefined undefined baz不会自动往下找 // 这里baz是undefined是因为,变量的提升和赋值不是同时进行的 // 先执行new foo,才执行了var baz = 赋值
-
-
箭头函数
-
箭头函数会无视以上所有的规则,this的值就是函数创建时候所在的lexical scope中的this
-
例子
function Person(){ this.age = 0; setTimeout(function () { console.log(this.age); // 输出undefined }, 1000); } var p = new Person(); function Person(){ this.age = 10; setTimeout(()=> { console.log(this.age); // 输出10 }, 1000); } var p = new Person(); -
箭头函数中的this是在定义函数的时候绑定,而不是在执行函数的时候绑定
-
箭头函数中,this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正式因为它没有this,所以也就不能作构造函数,不能写一个箭头函数,然后new这个函数
-
箭头函数中的this是在定义函数的时候绑定
-
例子
var x = 11 var obj = { x: 22, say: () => { console.log(this.x) // 11 } } obj.say()-
所谓的定义时候绑定,就是this是继承自父执行上下文中的this,比如这里的箭头函数中的this.x,箭头函数本身与say平级义key: value的形式,也就是箭头函数本身所在的对象为obj,而obj的父执行上下文就是window,因此这里的this.x实际上表示的是window.x,因此输出是11
-
关于为什么这个箭头函数的平级是obj
var name = 'xi' var age = 17 var obj = { name: 'xx', objAge: this.age, myFun: function () { console.log(this.name + this.age) } } console.log(obj.objAge) // 输出是17,因为平级的this指向obj obj.myFun() // 输出是xx undefined
-
-
例子
var obj = { birth: 1990, age: function() { var b = this.birth var fn = () => new Date.getFullYear() - this.birth // this指向obj return fn() } } obj.getAge() // 31- 例子中箭头函数本身是在getAge方法中定义的,因此,getAge方法的父执行上下文是obj,因此这里的this指向则为obj对象
-
例子
var a = 1 var obj = { a: 2, fun: () => { var obj = { a: 3, fun: () => { console.log(this.a) } } obj.fun() } } obj.fun() // 1
-
-
-
-
用call方法考虑调用对象
-
在函数中直接使用
-
例子
function get(content){ console.log(content) } get('你好') // 是下面的简写 get.call(window, '你好')
-
-
函数作为对象的方法被调用(谁调用我,我就指向谁)
-
例子
var person = { name: '张三', run: function(time) { console.log(`${this.name}在跑步 最多${time}min就不行了`) } } person.run(30) person.run.call(person, 30)
-
-
例子
// 问题 var name = 222 var a = { name: 111, say: function () { console.log(this.name) } } var fun = a.say fun() a.say() var b = { name: 333, say: function (fun) { fun() } } b.say(a.say) b.say = a.say b.say() // 答案 var name = 222 var a = { name: 111, say: function () { console.log(this.name) } } var fun = a.say fun() // 函数的直接使用,是fun.call(window)的语法糖,那么this指向window,222 a.say() // a.say.call(a),指向a,111 var b = { name: 333, say: function (fun) { fun() // fun.call(window),指向的是window,222 } } b.say(a.say) // 不管,直接看fun b.say = a.say b.say() // 指向的是b,333var name = 222 var a = { name: 111, say: function () { console.log(this.name) } } var b = { name: 333, say: function (fun) { fun() // 因为fun.call(window) // 另一种说法:函数里的函数指向window } } b.say(a.say) // 222 var c = { name: 444, say: function() { function test() { console.log(this.name) } test() // 因为test.call(window) } } c.say() // 222
-