1. 手写call方法
手写call思路: 对象通过 Object.attributes 来访问自身的属性,这是给对象添加一个属性,该属性指向需绑定对象的函数上。通过 Object.attributes 去调用函数,函数中的 this 指向调用它的对象,最后将属性从对象中移除。
Function.prototype.myCall = function(context) {
const self = context || window;
let arg = Array.prototype.slice.call(arguments,1)
self.f = this;
self.f(...arg);
delete self.f;
}
// 测试
function getUser(age){
console.log(this.name);
console.log(age)
}
let user = {
name: 'Jason'
}
getUser.myCall(user,24)
1.1 arguments 类似数组转换为数组的方法
//方法一
let arg = Array.prototype.slice.call(argment,1)
//方法二
let arg = Array.prototype.concat.apply([],arguments)
//方法三
let arg = Array.from(arguments)
// 方法四
let arg = [...arguments]
1.2 .... 展开语法传递参数
Function.prototype.myCall = function(context, ...item) {
const self = context || window;
self.f = this;
self.f(...item);
delete self.f;
}
// 测试
function getUser(age){
console.log(this.name);
console.log(age);
}
let user = {
name: 'Jason'
}
getUser.myCall(user,24)
如上代码中的 const self = this || window 是用指定传入的对象为null时,默认为 window对象
2. 手写apply方法
apply 与 call 的实现方式相同,apply 只能出入两个参数, 第一个参数为绑定的 this 对象,第二个参数为一个数组,作为函数的参数。
Function.prototype.myApply = function(context, arr) {
const self = context || window;
if(!Array.isArray(arr)){
throw new TypeError("传入的参数类型不正确,传入一个数组");
}
self.f = this;
self.f(...arr);
delete self.f;
}
// 测试
function getAge(age){
console.log(age)
}
getAge.myApply(null, 24)
2.1 arguments 实现 apply 可传递多个参数(其实的是一个伪数组对象)
Function.prototype.myApply = function(context) {
const self = context || window;
let arg = Array.prototype.slice.call(arguments,1)
if(!Array.isArray(arg)){
throw new TypeError("传入的参数类型不正确,传入一个数组");
}
self.f = this;
self.f(...arg);
delete self.f;
}
// 测试
function getAge(age){
console.log(age)
}
getAge.myApply(null, 24)
2.2 ... 展开语法实现 apply 传递多个参数(其实传入了一个数组)
Function.prototype.myApply = function(context,...item) {
const self = context || window;
if(!Array.isArray(item)){
throw new TypeError("传入的参数类型不正确,传入一个数组");
}
self.f = this;
self.f(...item);
delete self.f;
}
// 测试
function getAge(age){
console.log(age)
}
getAge.myApply(null, 24)
3. 手写bind方法
bind 与call,apply 不同在于函数执行后将创建一个新函数返回。bind 函数可传入两个参数,第一个参数是 this 绑定的对象, 第二个参数是一个数组或者类数组对象,第二个参数将作为实参在新函数执行时被传入。
Function.prototype.myBind = function(context){
const obj = context || window;
const self = this;
let arg = Array.prototype.slice.call(arguments, 1);
return function(){
let arg2 = Array.prototype.slice.call(arguments, 1);
self.apply(obj, arg.concat(arg2));
}
}
如上 mybind 创建的新函数作为普通函数调用时 this 指向你所绑定的对象。当做为构造函数通过 new 去创建实例对象时 this 绑定的对象就 会被忽略了。因为构造函数中的会隐式创建一个实例对象,this 指向这个实例对象。而不再是指向你所指定的对象。
Function.prototype.myBind = function(context){
const obj = context || window;
const self = this;
let arg = Array.prototype.slice.call(arguments, 1);
const newFun = function(){
let arg2 = Array.prototype.slice.call(arguments, 1);
if(this instanceof newFun) {
self.apply(this, arg.concat(arg2));
} else {
self.apply(obj, arg~~~~.concat(arg2));
}
}
//支持 new 创建对象时,实例__proto__指向绑定对象函数的prototype
newFun.prototype = this.prototype
return newFun;
}
// 测试
let user = {
name: 'Jason',
age: 23,
}
function Fun(school) {
this.school = school;
console.log(this.name);
console.log(this.age);
console.log(this.school);
}
let fun = Fun.myBind(user, '江南大学');
// this -> user
fun()
// this -> 对象实例
let obj = new fun();
console.log(obj.__proto__ === Fun.prototype) // ture