本文深入解析JavaScript中this的指向机制,对比call、apply与bind的区别,重点阐述如何手动实现ES5标准的bind方法。内容涵盖参数预绑定、执行时参数合并、this指向的动态处理以及原型链的完整继承,通过代码示例演示如何解决常见面试题,确保新实例能正确识别构造函数与原型。
前言与this指向概述
前言
在JavaScript中,this的指向往往令人困惑,但它在众多场景中不可或缺,因此理解并掌握this的使用至关重要。本文不赘述this的基础概念,而是聚焦于如何主动改变this的指向。
改变this指向主要有三种方式:call、apply和bind。接下来将逐一介绍这三种方法的具体用法。
改变this指向的三种方法
改变this指向
call
var a = {
name:"aaa",
say(type){
console.log(type,this.name);
}
}
a.say("at");//at aaa
var tn = {name:"ttt"};
a.say.call(tn,"tt")//tt ttt
通过call方法,可以看到say函数中的this指向了tn对象,同时演示了如何传递参数。
apply
var a = {
name:"aaa",
say(type){
console.log(type,this.name);
}
}
a.say("at");
var tn = {name:"ttt"};
a.say.apply(tn,["tt"])
通过apply方法,say函数中的this同样指向了tn,其区别在于参数必须以数组形式传递。
bind
bind方法也能改变this的指向,但与call和apply不同,bind不会立即执行函数,而是返回一个新的函数。
var a = {
name:"aaa",
say(type){
console.log(type,this.name);
}
}
var tn = {name:"ttt"};
var b = a.say.bind(tn);
b();//ttt
bind在改变this的同时,还能保持原型链的继承关系,具体代码如下。
var to = {name:"to",color:"red"};
function Animal(){
console.log(`name:${this.name}...color:${this.color}`);
}
Animal.prototype.say = function(){
console.log(`say..name:${this.name}...color:${this.color}`);
}
var Cat = Animal.bind(to);
Cat();//name:to...color:red
var cat = new Cat();// name:undefined...color:undefined
cat.say();//say..name:undefined...color:undefined
由于cat是Cat的实例,而Cat是通过bind改变this后的Animal,因此cat也是Animal的实例。然而,此时this指向的是cat本身,所以直接访问this.name会返回undefined。
手动实现ES5 bind方法
实现bind
Function.prototype.bind = function(obj){
const args = Array.prototype.slice.call(arguments,1);//保留bind时的参数
const that = this;
const bound = function(){
const inArgs = Array.prototype.slice.call(arguments);//执行bind的函数时的参数
const newArgs = args.concat(inArgs);//组装参数
that.apply(obj,newArgs);//执行bind的函数
}
//继承prototype--寄生组合式继承
function F(){};
F.prototype = that.prototype;
bound.prototype = new F();
return bound;
}
执行上述代码的结果如下。
Cat();//name:to...color:red
var cat = new Cat();//name:to...color:red
cat.say();//say..name:undefined...color:undefined
需要注意的是,第二行输出与原生bind的行为略有差异。这里保留了之前bind绑定的对象,而原生bind在某些情况下可能表现为undefined,具体原因值得探究。
bind方法面试题解析
面试题
实现ES5中的bind方法,使得以下代码能够输出success。
function Animal(name,color){
this.name = name;
this.color = color;
}
Animal.prototype.say = function(){
return `i am a ${this.color} ${this.name}`
}
const Cat = Animal.bind(null,"cat");
const cat = new Cat("white");
if(cat.say() === 'i am a white cat' && cat instanceof Cat && cat instanceof Animal){
console.log("success")
}
结合上述bind的实现,为什么没有输出success?原因何在?
分析代码发现,bind的第一个参数传入了null。当参数为null时,默认应使用当前的this。修改后的代码如下。
Function.prototype.bind = function(obj){
const args = Array.prototype.slice.call(arguments,1);//保留bind时的参数
const that = this;
const bound = function(){
const inArgs = Array.prototype.slice.call(arguments);//执行bind的函数时的参数
const newArgs = args.concat(inArgs);//组装参数
const bo = obj || this;
that.apply(bo,newArgs);//执行bind的函数
}
//继承prototype--寄生组合式继承
function F(){};
F.prototype = that.prototype;
bound.prototype = new F();
return bound;
}
最终输出success。
完美解决~~~
撒花庆祝~~~
总结
以上就是本文的全部内容,希望这些内容对您的学习或工作有所帮助。如有任何疑问,欢迎留言交流,感谢大家对本站的支持。