最近面试的心得,前端还是得学精学细啊,所以我打算手动造几个轮子
知识点准备
手写一个call
// An highlighted block
var Person = {
name: 'Richard',
say() {
console.log(this);
console.log(`我叫${this.name}`)
},
}
var Person1 = {
name: 'Silcence'
}
Person.say.call(Person1); //我叫Silence
// An highlighted block
Function.prototype.Mycall = function(context){
console.log(this); //在Function类的原型上是否有say
//如果有, context.say 就是this
context.say = this;
context.say();
}
Person.say.Mycall(Person1)
在Function的原型上找到say方法,并赋予context
-
问题所在
-
未考虑无参数传递时的情况
未传参时 this指向Window -
未考虑多个参数的情况,若有多个参数应把参数传递给扩展方法
-
给上下文定义的应是一个唯一的方法
-
-
处理后的版本
- Symbol()函数会返回symbol类型的值,每个从Symbol()返回的值都是唯一的
console.log(Symbol(‘foo’) === Symbol(‘foo’));
var Person = { name: 'Richard', say(name2) { console.log(this); console.log(`我叫${this.name}`) console.log(`这是传入的参数:${name2}`); }, } var Person1 = { name: 'Silcence' } Function.prototype.Mycall = function(context){ console.log(this); //在Function类的原型上是否有say //如果有, context.say 就是this context = context||window // 参数为空时的处理 let fn = Symbol(context); //让context唯一 context.fn = this; let arg = [...arguments].slice(1); //除掉第一个参数,剩余的为传入参数 ...把类数组转换为真正的数组 context.fn(...arg)//执行fn delete context.fn //删除方法 } //Person.say.call(Person1); //自定义call方法 Person.say.Mycall(Person1,'八哥');
手写一个apply
-
ES6扩展运算符
具体用法参照此篇文章 ES6扩展运算符
和call方法大致相同,传参时第二个参数为数组
var Person = { name: 'Richard', say(name2) { console.log(this); console.log(`我叫${this.name}`) console.log(`这是传入的参数:${name2}`); }, } var Person1 = { name: 'Silcence' } Function.prototype.Mycall = function(context){ console.log(this); //在Function类的原型上是否有say //如果有, context.say 就是this context = context||window // 参数为空时的处理 let fn = Symbol(context); //让context唯一 context.fn = this; let arg = [...arguments].slice(1); context.fn(...arg)//执行fn delete context.fn //删除方法 } //Person.say.call(Person1); //自定义call方法 Person.say.Mycall(Person1,['八哥']);
- Symbol()函数会返回symbol类型的值,每个从Symbol()返回的值都是唯一的
手写一个Bind
-
Bind的定义
fun.bind(thisArg[, arg1[, arg2[, …]]])
- bind() 函数会创建一个新的绑定函数
- 注意,一个 绑定函数 也能使用 new 操作符创建对象,这种行为就像把原函数当成构造器,thisArg 参数无效。也就是 new 操作符修改 this 指向的优先级更高。
-
初步实现
var Person = {
name: 'Richard',
say(name2) {
console.log(this);
console.log(`我叫${this.name}`)
console.log(`这是传入的参数:${name2}`);
},
}
var Person1 = {
name: 'Silcence'
}
Function.prototype.myBind = function(thisArg){
if (typeof this!== 'function'){
return;
}
var that = this;
var args = [...arguments].slice(1)
return function(){
//因为同样支持柯里化形式的传参,我们需要再次存储参数
let newArg = [...arguments]
console.log(newArg)
return that.apply(thisArg,args.concat(newArg));
}
}
var result = Person.say.myBind(Person1)
result('八哥');
关于柯里化传参
Function.prototype.bind2 = function(context) {
if (typeof this !== "function") {
throw new TypeError("Error");
}
const that = this;
// 保留之前的参数,为了下面的参数拼接
const args = [...arguments].slice(1);
return function F() {
// 如果被new创建实例,不会被改变上下文!
if (this instanceof F) {
return new that(...args, ...arguments);
}
// args.concat(...arguments): 拼接之前和现在的参数
// 注意:arguments是个类Array的Object, 用解构运算符..., 直接拿值拼接
return that.apply(context, args.concat(...arguments));
};
};
/**
* 以下是测试代码
*/
function test(arg1, arg2) {
console.log(arg1, arg2);
console.log(this.a, this.b);
}
const test2 = test.bind2(
{
a: "a",
b: "b"
},
1
); // 参数 1
test2(2); // 参数 2