this
定义
当一个函数被调用时,会创建执行上下文,包含函数在哪里被调用、函数的调用方式、传入的参数等信息。this就是某中一个属性,会在函数执行的过程中用到。
this是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。
参考
- this - MDN
绑定规则
正常函数
优先级由高到低
- new绑定,绑定创建的对象
- 显示绑定(call,apply),绑定指定的对象
- 隐式绑定,某个上下文对象中调用,绑定上下文对象
- 默认绑定,绑定全局对象
箭头函数
编译时绑定的,根据外层(函数或全局)作用域来决定,箭头函数的绑定无法被修改。
INFO
- 没有this、arguments、super
- this指向永远不变,
- 不能用作构造函数,没有new.target
- 没有原型
参考
- 箭头函数表达式 - MDN
- ES6 系列之箭头函数 - 冴羽 GitHub
参考
- JavaScript 深入之史上最全--5 种 this 绑定全面解析 - 木易杨 博客
你不知道的JavaScript【上】,第2部分第2章
- JavaScript 深入之重新认识箭头函数的 this - 木易杨 博客
- 你不得不懂的 JS this 指向 - 林一一 掘金
- ⌛️【建议 👍】再来 40 道 this 面试题酸爽继续(1.2w 字用手整理) - 掘金
题目
js
var name = 'window'
var obj = {
name: 'obj',
prop: {
getName: function () {
return this.name
},
},
getName: function () {
return this.name
},
}
// console.log(obj.prop.getName()) undefined
// console.log(obj.getName()) obj
var a = obj.prop.getName
// console.log(a()) window
js
var val = 10
let a = function () {
console.log(this.val)
}
a.prototype.val = 9
val = 3
a() // 3
let b = new a() // 9
console.log(b.val) // 9
js
var name = 'window'
var person1 = {
name: 'person1',
show1: function () {
console.log(this.name)
},
show2: () => console.log(this.name),
show3: function () {
return function () {
console.log(this.name)
}
},
show4: function () {
return () => console.log(this.name)
},
}
var person2 = { name: 'person2' }
person1.show1() // person1
person1.show1.call(person2) // person2
person1.show2() // window
person1.show2.call(person2) // window
person1.show3()() // window
person1.show3().call(person2) // person2
person1.show3.call(person2)() // window
person1.show4()() // person1
person1.show4().call(person2) // person1
person1.show4.call(person2)() // person2
js
var name = 'window'
function Person(name) {
this.name = name
this.show1 = function () {
console.log(this.name)
}
this.show2 = () => console.log(this.name)
this.show3 = function () {
return function () {
console.log(this.name)
}
}
this.show4 = function () {
return () => console.log(this.name)
}
}
var personA = new Person('personA')
var personB = new Person('personB')
personA.show1() // personA
personA.show1.call(personB) // personB
personA.show2() // personA
personA.show2.call(personB) // personA
personA.show3()() // window
personA.show3().call(personB) // personB
personA.show3.call(personB)() // window
personA.show4()() // personA
personA.show4().call(personB) // personA
personA.show4.call(personB)() // personB
js
var num = 1
var myObject = {
num: 2,
add: function () {
this.num = 3
;(function () {
console.log(this.num)
this.num = 4
})()
console.log(this.num)
},
sub: function () {
console.log(this.num)
},
}
myObject.add() // 1 3
console.log(myObject.num) // 3
console.log(num) // 4
var sub = myObject.sub
sub() // 4
js
var a = 2
var obj = {
a: 4,
fn1: (function () {
this.a *= 2
var a = 3
return function () {
this.a *= 2
a *= 3
console.log(a)
}
})(),
}
var fn1 = obj.fn1 // 【this -> window】
console.log(a) // 4
fn1() // 9 【闭包 this-> window】
obj.fn1() // 27
console.log(a) // 8
console.log(obj.a) //8
js
var n = 2 // -> 4 -> 8
var obj = {
n: 3, // 6
fn: (function (n) {
n *= 2
this.n += 2 // window 下的 n 变成 4
var n = 5 // 这一步不会再重新声明,因为已经参数赋值,就不会再声明而是直接赋值 n = 5
console.log('window.n', window.n)
return function (m) {
console.log('n:', n, 'm', m) // n:5 m:3 这里的 n 向上查找是 5 //
this.n *= 2 // fn(3): 2 * 4 =8 // obj.fn(3): 2 * 3 = 6
console.log(m + ++n) // 3 + (++5) ++n 导致上级作用域的n变成了6 // 3 + (++6)
}
})(n),
}
var fn = obj.fn
fn(3) // 9
obj.fn(3) // 10
console.log(n, obj.n) // 8 6
js
var num = 10 // 60; 65
var obj = {
num: 20,
}
obj.fn = (function (num) {
this.num = num * 3
num++ // 21
return function (n) {
this.num += n // 60 + 5 = 65;20 + 10 =30
num++ // 21 + 1 = 22;22 + 1 = 23
console.log(num)
}
})(obj.num)
var fn = obj.fn
fn(5) // 22
obj.fn(10) // 23
console.log(num, obj.num) // 65, 30
call、apply
javascript
Function.prototype._call = function (ctx) {
// 如果不为空,则需要进行对象包装
const o = ctx ? Object(ctx) : window
// 给 ctx 添加独一无二的属性
const fn = Symbol()
o[fn] = this
// 执行函数,得到返回结果
const args = [...arguments].slice(1)
const result = o[fn](...args)
// 删除该属性
delete o[fn]
return result
}
javascript
Function.prototype._apply = function (context, arr) {
context = context ? Object(context) : window
context.fn = this //用symbol
let result
if (!arr) {
result = context.fn()
} else {
result = context.fn(...arr)
}
delete context.fn
return result
}
参考
- Function.prototype.call() - MDN
- Function.prototype.apply() - MDN
- Arguments 对象 - MDN
- 深度解析 call 和 apply 原理、使用场景及实现 - 木易杨 博客
- JavaScript深入之call和apply的模拟实现 - 冴羽 GitHub
- 深入了解 call, apply, bind 和模拟实现 - 林一一 掘金
bind
js
Function.prototype._bind = function (context) {
if (typeof this !== 'function') {
throw new Error(
'Function.prototype.bind - what is trying to be bound is not callable',
)
}
var self = this
var args = Array.prototype.slice.call(arguments, 1)
var fNOP = function () {}
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments)
return self.apply(
this instanceof fNOP ? this : context,
args.concat(bindArgs),
)
}
fNOP.prototype = this.prototype
fBound.prototype = new fNOP()
return fBound
}
js
var bar = function () {
console.log(this.x)
}
var foo = {
x: 3,
}
var sed = {
x: 4,
}
var func = bar.bind(foo).bind(sed)
func() // 3
var fiv = {
x: 5,
}
var func = bar.bind(foo).bind(sed).bind(fiv)
func() // 3
参考
- Function.prototype.bind() - MDN
- 深度解析bind原理、使用场景及模拟实现 - 木易杨 博客
- JavaScript深入之bind的模拟实现 - 冴羽 GitHub