this知识点总结
this 知识点总结
this是js中比较重要的一个点,理论其实是很简单的,但用起来很容易出错。
this规则
首先this概念是针对对象的,函数与函数之间的调用不存在this关系。同时this的确定是在运行时才能确定,和作用域的编译阶段确认不同。最基本的规则是:当调用者(对象)调用函数时,函数内部的this就指向调用者,否则指向undefined(非严格状态下指向window对象)。除最基本的规则外,this可以被显式的改变,最典型的就是bind、call、apply方法。
this基本规则解析
根据定义,这里套用别人的例子。
- 普通this示例。示例中getA属于obj对象,当obj调用getA方法时,方法是obj拥有的方法,此时显示obj中的a。当getA被取出时,此时getA指向的是函数getA所在的地址,提取出来的getA与obj脱离了关系,调用者不再是obj,此时this指向了window。
var a = 1
var obj = {
a: 10,
getA: function () {
console.log(this.a)
},
}
obj.getA() // 10
var getA = obj.getA
getA() // 1
- 箭头函数下示例。箭头函数没有this和arguments,它的this是共享父级或父级的父级作用域下的this。如下面的例子,getA由于是箭头函数,它共用了父级(window)的作用域下的this,而此时即使在严格模式下this也是指向window的。而parentA是普通函数,它的this指向的是obj,内部的箭头函数this指向的是parentA的this,也就是obj。
'use strict'
var a = 1
var obj = {
a: 10,
getA: () => {
console.log(this.a)
},
parentA: function () {
(() => {
console.log(this.a)
})()
}
}
obj.getA() // 1
obj.parentA() // 10
new对象的this指向
javascript中的new发生了这些(原文):
-
创建了一个新对象,挂载原型链。
-
调用构造函数,将this指向我们新建的对象。这样我们可以在new出来的对象上挂载新的变量和函数。
-
构造函数如果返回了一个对象,那么new操作符返回的是这个对象。如果构造函数没有返回对象,那么new操作符返回new创建的新对象。
所以我们新生成的对象下的方法和原型链上的方法,当对象调用这些方法时,this指向的就是我们新的对象,即谁调用指向谁(仅限于对象)。
显式绑定this
显示绑定this的方法包括:bind,call,apply。call和apply是在调用函数时进行动态绑定,而bind会返回一个被绑定到指定对象的新函数。如果绑定的null则会指向原来的this。
(示例取自You-Dont-Know-Js)[https://github.com/getify/You-Dont-Know-JS/blob/master/this %26 object prototypes/ch2.md]
- call、apply、bind优先级的确认
示例:
function foo() {
console.log( this.a );
}
var obj1 = {
a: 2,
foo: foo
};
var obj2 = {
a: 3,
foo: foo
};
obj1.foo(); // 2
obj2.foo(); // 3
obj1.foo.call( obj2 ); // 3
obj2.foo.call( obj1 ); // 2
var baz = foo.bind(obj1)
baz() // 2
baz.call(obj2) // 2
call和apply调用时函数的this绑定到了call或apply的第一个参数,而bind返回的函数则不管函数如何调用,函数内的this都指向了bind时的参数。也是就是bind的优先级要高于call和apply
-
new优先级的确认
看下面的示例。示例中new之后,如果是非显示绑定,那么this指向的是new对象,如果是对象的方法显示绑定后,指向的是绑定后的值。说明new的优先级应该是最低的。
function Foo (a) {
this.a = a
}
var obj = {
a: 100
}
var obj0 = {
a: 0
}
Foo.prototype.bool = function () {
console.log(this.a)
}
Foo.prototype.bindFunc = function () {
console.log(this.a)
}.bind(obj)
var obj1 = new Foo(1)
obj1.bool() // 1
obj1.bool.call(obj) // 100
obj1.bindFunc() // 100
obj1.bindFunc.call(obj0) // 100
再看一个例子。这个例子中的bind被用于构造函数。此时Foo函数中的this指向的并不是bindObj,如果是的话bindObj会被设置为11。说明new操作符在构造函数中的优先级是高于bind的。
function Foo (a) {
this.a = a
}
var bindObj = {
a: 22
}
Foo.prototype.getA = function () {
return this.a
}.bind(bindObj)
var Bool = Foo.bind(bindObj)
var obj = new Bool(11)
console.log(bindObj.a) // 22
console.log(obj.a) // 11
console.log(obj.getA()) // 22
我们实现的普通的bind和浏览器不同:
function bind(fn, obj) {
return function() {
fn.apply( obj, arguments );
};
}
这种写法和浏览器实现是有差距的,所以才polyfill才会比较复杂
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError( "Function.prototype.bind - what " +
"is trying to be bound is not callable"
);
}
var aArgs = Array.prototype.slice.call( arguments, 1 ),
fToBind = this,
fNOP = function(){},
fBound = function(){
return fToBind.apply(
(
this instanceof fNOP &&
oThis ? this : oThis
),
aArgs.concat( Array.prototype.slice.call( arguments ) )
);
}
;
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
call和apply对比
这块不属于this部分了。主要是对比一下差别。从处理流程上来讲,call是比apply快的。可以看下apply定义https://tc39.github.io/ecma262/#sec-function.prototype.apply
apply 要额外进行一次CreateListFromArrayLike(argArray)。就是要将数组或类似数组的对象转换为内部使用的call所要的list,而call方法则直接把所有参数Push进list。性能是有一部分差距,但没必要特意关注,如果已经是数组类型直接用apply即可,如果是个数少用call也可以,手动去转换类型反而会拖慢速度。

浙公网安备 33010602011771号