关注「松宝写代码」,精选好文,每日一题
作者:saucxs | songEagle
2020,实「鼠」不易
2021,「牛」转乾坤
风劲潮涌当扬帆,任重道远须奋蹄!
一、前言
2020.12.23 立的 flag,每日一题,题目类型不限制,涉及到JavaScript,Node,Vue,React,浏览器,http等领域。
本文是「每日一题」第 21 题:【每日一题】(21题)面试官问:谈谈JS中的 this 的绑定?
这是一道很宽泛的题目,如何回答更有亮点?

二、概括
1、改变this绑定的主要方式
1)最常用的call()、apply()、bind()方法,如:
function foo () {
this.count++
}
const obj = {
count: 10
}
foo.call(obj)
console.log(obj.count) // 11
2)new绑定
let count = 10
function Foo (count) {
this.count = count
}
let obj = new Foo(20)
console.log(count, obj.count) // 10 20
在使用new初始化类时,会调用类中的构造函数,这个过程自动执行了以下操作:
- 创建一个新对象
- 将构造函数中的作用域赋给新对象,即
this指向新对象 - 执行构造函数中的代码
- 返回一个新对象
在函数内部有两个不同的内部方法:[[Call]]、[[Construct]]:
- 当使用new关键字调用函数时,执行
[[Construct]]函数,负责创建一个实例,然后执行函数体,将this绑定到实例上;此时new.target被赋值为new操作符的目标,也就是新创建的实例。 - 非new调用的函bb数,执行
[[Call]]函数,直接执行函数体;new.target值undefined。
注意:不是所有的函数都存在[[Construct]]方法,如箭头函数就没有,因为箭头函数也不能通过new调用。
3)在函数引用存在上下文时,会将函数调用中的this绑定到上下文对象,且只会绑定到引用链中的最后一层。
let count = 10
function foo () {
this.count++
}
let obj = {
count: 20,
foo: foo v
}
obj.foo()
console.log(count, obj.count) // 10 21
4)箭头函数以及ES6中类的箭头函数属性,在使用箭头函数时,上下文取决于最近的非箭头函数的作用域
5)双冒号运算符,ES6引入来代替bind绑定
6)ES6中类的箭头函数属性也可以实现更改this的绑定
2、call、apply、bind的用法与区别
bind:
- bind绑定后,第一次之后的其他绑定均无法生效
共同点:
- 都是实现this绑定的切换
- 当传入的第一个参数为基本类型时,会先转换成为引用类型,当参数不存在或着为null时,均会报错;
区别:
- call与apply唯一的区别是:接收作为函数执行参数的格式不同,apply必须是数组的格式,call则是按顺序接收多个参数
- call与apply会立即调用;而bind()方法返回一个新的函数,需要触发才可执行,在运行时,原本bind()传递的除第一个之外的参数 + 运行时传入的参数,按顺序,作为被执行函数的参数
3、bind绑定原理
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 () {
// this instanceof fNOP === true时,说明返回的fBound被当做new的构造函数调用
return fToBind.apply(this instanceof fNOP
? this
: oThis,
// 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
aArgs.concat(Array.prototype.slice.call(arguments)))
}
// 维护原型关系
if (this.prototype) {
// Function.prototype doesn't have a prototype property
fNOP.prototype = this.prototype
}
// 下行的代码使fBound.prototype是fNOP的实例,因此
// 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
fBound.prototype = new fNOP()
return fBound
}
}
往期「每日一题」
1、JavaScript && ES6
-
第 39 题:【每日一题】(39题)谈谈JS的函数扩展?
-
第 30 题:【每日一题】(30题)面试官:ES6的解构赋值的理解?
-
第 16 题:【每日一题】面试官问:JS中如何全面进行客户端检测?
-
第 15 题:【每日一题】面试官问:JS类型判断有哪几种方法?
-
第 14 题:【每日一题】面试官问:谈谈你对JS对象的创建和引申
-
第 12 题[每日一题]面试官问:JS引擎的执行过程(二)
-
第 11 题[每日一题]面试官问:JS引擎的执行过程(一)
-
第 10 题[每日一题]面试官问:详细说一下JS数据类型
2、浏览器
3、Vue
4、React
5、HTML5
6、算法
-
第 31 道[【每日一题】(31题)面试官:你对图论了解多少?(一)
-
第 26 道【每日一题】(26题)算法题:最长公共前缀?
7、Node
8、Http
谢谢支持
1、文章喜欢的话可以「分享,点赞,在看」三连哦。
2、作者昵称:saucxs,songEagle,松宝写代码。「松宝写代码」作者,每日一题,实验室等。一个爱好折腾,致力于全栈,正在努力成长的字节跳动工程师,星辰大海,未来可期。内推字节跳动各个部门各个岗位。
3、长按下面图片,关注「松宝写代码」,是获取开发知识体系构建,精选文章,项目实战,实验室,每日一道面试题,进阶学习,思考职业发展,涉及到JavaScript,Node,Vue,React,浏览器,http,算法,端相关,小程序等领域,希望可以帮助到你,我们一起成长~
