1、bind模拟实现
bind作用
bind() 方法会创建一个新函数。
当这个新函数被调用时,bind() 的第一个参数将作为它运行时的this,
之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )
复制代码
1、返回新的函数
2、传递参数
第一步,返回新的函数
Function.prototype.bind1 = function(context){
let self = this;
return function (){
return self.apply(context);
}
}
复制代码
第二步,传递参数
Function.prototype.bind1 = function(context){
let self = this;
return function (){
let args2 = Array.prototype.slice.call(arguments, 1);
return self.apply(context, args2);
}
}
复制代码
绑定的时候传递一部分参数,调用的时候传递一部分参数
Function.prototype.bind1 = function(context){
let self = this;
let args1 = Array.prototype.slice.call(arguments, 1);
return function (){
let args2 = Array.prototype.slice.call(arguments);
return self.apply(context, args1.concat(args2));
}
}
复制代码
第三步,构造函数的效果模拟
当 bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效,但传入的参数依然生效。
Function.prototype.bind1 = function(context){
let self = this;
let args1 = Array.prototype.slice.call(arguments, 1);
let fBind = function (){
let args2 = Array.prototype.slice.call(arguments);
return self.apply(this instanceof fBind ? this: context, args1.concat(args2));
}
fBind.prototype = this.prototype;
return fBind;
}
复制代码
第四步,构造函数的效果优化
但是在这个写法中,我们直接将fBind.prototype=this.prototype,我们直接修改 fBind.prototype的时候,也会直接修改绑定函数的prototype。这个时候,我们可以通过一个空函数来进行中转:
Function.prototype.bind2 = 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 fBind = function () {
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBind.prototype = new fNOP();
return fBind;
}
复制代码
2、call的模拟实现
call() 方法在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法。
1、改变this指向
2、调用函数
第一步,改变this指向
模拟的步骤可以分为:
1)将函数设为对象的属性
2)执行该函数
3)删除该函数
Function.prototype.call1 = function(context){
//this指向绑定函数
//context指向传递的第一个参数
context.fn = this;
context.fn();
delete context.fn;
}
var foo = {
value: 1,
};
function bar(){
console.log(this.value)
}
bar.call1(foo); //1
复制代码
第二步,传递参数
Function.prototype.call1 = function(context){
//this指向绑定函数
//context指向传递的第一个参数
context.fn = this;
let args = [];
for(let i = 1, len = arguments.length; i < len; i++){
// 执行后 args为 ["arguments[1]", "arguments[2]", "arguments[3]"]
args.push('arguments['+ i +']');
}
eval('context.fn('+ args +')');
delete context.fn;
}
var foo = {
value: 1,
};
function bar(a, b){
console.log(this.value, a ,b)
}
bar.call1(foo, 2, 3);//1,2,3
复制代码
第三步,有返回值,或this为null时指向window
Function.prototype.call1 = function(context){
//this指向绑定函数
//context指向传递的第一个参数
context = context || window;
context.fn = this;
let args = [];
for(let i = 1, len = arguments.length; i < len; i++){
// 执行后 args为 ["arguments[1]", "arguments[2]", "arguments[3]"]
args.push('arguments['+ i +']');
}
let result = eval('context.fn('+ args +')');
delete context.fn;
return result;
}
var foo = {
value: 1,
};
var value = 2;
function bar(a, b){
console.log(this.value, a ,b)
}
bar.call1(null, 2, 3);
复制代码
3、apply的模拟实现
类似于call,只是apply参数为数组
Function.prototype.apply1 = function (context, arr) {
//this指向绑定函数
//context指向传递的第一个参数
context = context || window;
context.fn = this;
let result;
if(!arr){
result = context.fn();
} else {
let args = [];
for (let i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')');
}
delete context.fn;
return result;
}
var foo = {
value: 1,
};
var value = 2;
function bar(a, b){
console.log(this.value, a ,b)
}
bar.apply1(null, [2, 3]);//2,2,3
复制代码
4、new的模拟实现
new的作用创建对象类型的实例
// Otaku 御宅族,简称宅
function Otaku (name, age) {
this.name = name;
this.age = age;
this.habit = 'Games';
}
// 因为缺乏锻炼的缘故,身体强度让人担忧
Otaku.prototype.strength = 60;
Otaku.prototype.sayYourName = function () {
console.log('I am ' + this.name);
}
var person = new Otaku('Kevin', '18');
console.log(person.name) // Kevin
console.log(person.habit) // Games
console.log(person.strength) // 60
person.sayYourName(); // I am Kevin
复制代码
从上面看出,实例person具可以:
1、访问到 Otaku 构造函数里的属性
2、访问到Otaku.prototype属性
第一步,基本实现
function objectFactory(){
let obj = new Object();
let Constructor = [].shift.apply(arguments);
//访问原型对象上的属性
obj.__proto__ = Constructor.prototype;
//访问构造函数上的属性
Constructor.apply(obj, arguments);
return obj;
}
复制代码
function Otaku(name, age) {
this.name = name;
this.age = age;
this.habit = 'Games';
}
Otaku.prototype.strength = 60;
Otaku.prototype.sayYourName = function () {
console.log('I am ' + this.name);
}
var person = objectFactory(Otaku, 'Kevin', '18')
console.log(person.name) // Kevin
console.log(person.habit) // Games
console.log(person.strength) // 60
person.sayYourName(); // I am Kevin
复制代码
第二步,返回值的效果
第一种情况,构造函数的返回值是对象形式,只能访问返回的对象中的属性
第二种情况,返回值为基本类型,与没有返回值一样,正常使用一样能方法原型对象属性和构造函数属性
function objectFactory(){
let obj = new Object();
let Constructor = [].shift.apply(arguments);
//访问原型对象上的属性
obj.__proto__ = Constructor.prototype;
//访问构造函数上的属性
let ret = Constructor.apply(obj, arguments);
return typeof ret === 'object' ? (ret || obj) : obj;
}
复制代码