javascript系列学习笔记-this
javascript系列学习笔记-this
kifjavascript系列学习笔记-this
笔记内容来自 《你不知道的Javascript》 (上)
为什么要用this
function getName() {
console.log('this.name', this.name)
}
let a = {
name: 'kif1'
}
let b = {
name: 'kif2'
}
getName.call(b)
上面代码可以在不同对象上下文(a,b)上使用 getName这个方法,不用针对每个对象编写不同版本的函数;
如果不用this,就需要给getName方法显示的传入一个上下文对象:
let a = {
name: 'kif1'
}
let b = {
name: 'kif2'
}
function getName(context) {
console.log('context.name', context.name)
}
getName(a)
this提供了一种更优雅的方式来隐式“传递”对象引用,可以让API设计得更加简洁和易于复用
绑定规则
默认绑定
最常用的调用规则:独立函数调用
function foo(){
console.log(this.a)
}
var a=2
foo() //2
上面代码中console.log(this.a) 会输出2,因为函数调用时应用了默认绑定,this指向了全局对象
如果开启严格模式,就无法使用默认绑定
隐式绑定
另一条需要注意的是调用位置是否否上下文对象,或者被对象包含或拥有
function foo() {
console.log(this.a)
}
let obj = {
a:12,
foo:foo
}
//调用位置
obj.foo() //12
调用位置会使用obj上下文来引用foo,有上下文时隐式绑定规则会将this绑定到这个上下文对象,
对象属性引用链只有最顶层或者说最后一层会影响调用位置,因此:
function foo() {
console.log(this.a)
}
let obj = {
a:12,
foo:foo
}
let obj2={
a:111,
obj:obj
}
obj2.obj.foo() //12
隐式丢失
常见的this绑定问题就是隐式丢失而应用了默认绑定,eg:
function foo() {
console.log(this.a)
}
let obj = {
a:12,
foo:foo
}
let fun = obj.foo
let a = 333
console.log(this.a) //333
显示绑定
call,apply,bind
上面隐式绑定需要在对象上包含一个指向函数的属性,并通过属性间接引用函数,,如果不想再对象属性里间接引用函数,而是在某个对象上强制调用某个函数,则需要 显示绑定
function foo(){
console.log(this.a)
}
let obj = {
a:123
}
foo.call(obj) //123
call和apply区别在于参数,第一个参数是原始值(字符串,数字,布尔)时,原始值会被转为对象形式,也就是 new String(),new Boolean,new Number(),这被成为 装箱
显示绑定无法解决上面的绑定丢失问题,但可以使用其变形:硬绑定
硬绑定
function foo(){
console.log(this.a)
}
let obj = {
a:123
}
let func = function(){
foo.call(obj)
}
func() //123
硬绑定后不能再修改!
典型应用:
包裹函数:
function foo(value){
console.log('value',value)
return this.a+value
}
let obj = {
a:123
}
let func = function(){
return foo.call(obj,...arguments)
}
let res = func(222)
console.log(res) //345(123+222)
硬绑定很常用,所以ES5中内置了Function.prototype.bind:
function foo(){
console.log(this.a)
}
let obj = {
a:123
}
let func = foo.bind(obj)
func() //123
new 绑定
function foo(a){
this.a=a
}
let bar = new foo(12)
console.log(bar.a)
优先级
显示绑定>new绑定>隐式绑定>默认绑定
判断this
是否是new绑定?是的话this指向新创建的对象
var bar = new foo()
是否通过call,apply(显示绑定)或者硬绑定(bind)?是的话this绑定的是指定对象
var bar = foo.call(obj)
函数是否在某个上下文对象中调用(隐式绑定)?是的话this绑定这个上下文对象
var bar = obj.foo()
都不是的话默认绑定,严格模式下绑定到undefined,否则绑定到全局对象
var = foo()
规则例外
被忽略的this
将null或者undefined作为参数传到call,apply,bind时,会被忽略而应用默认规则
手写call:
- 将函数设置为传入对象的属性;
- 执行该函数;
- 删除该属性;
Function.prototype.call1 = function () {
// 初始化,获取传入的this对象和后续所有参数
const [context, ...args] = [...arguments];
// 在传入的对象上设置属性为待执行函数
context.fn = this;
// 执行函数
const res = context.fn(args);
// 删除属性
delete context.fn;
// 返回执行结果
return res;
};