1、new的模拟实现
new的作用创建的实例对象
function Person (name, age) {
this.name = name;
this.age = age;
this.habit = 'games';
}
// 因为缺乏锻炼的缘故,身体强度让人担忧
Person.prototype.strength = 60;
Person.prototype.sayName = function () {
console.log('I am ' + this.name);
}
var person1 = new Person('Kevin', '18');
console.log(person1.name) // Kevin
console.log(person1.habit) // Games
console.log(person1.strength) // 60
person1.sayName(); // I am Kevin
从上面看出,实例对象person1可以:
- 1、访问到 Person 构造函数里的属性
- 2、访问到Person.prototype属性
- 3、构造函数如果返回值为对象,person1只能访问其对象上的属性。如果返回基本类型值,跟没有返回一样,正常使用
第一步,基本实现
function objectFactory(){
let obj = new Object();
let Constructor = [].shift.apply(arguments);
//访问原型对象上的属性
obj.__proto__ = Constructor.prototype;
//访问构造函数上的属性
Constructor.apply(obj, arguments);
return obj;
}
function (name, age) {
this.name = name;
this.age = age;
this.habit = 'games';
}
Person.prototype.strength = 60;
Person.prototype.sayName = function () {
console.log('I am ' + this.name);
}
var person1 = objectFactory(Otaku, 'Kevin', '18')
console.log(person1.name) // Kevin
console.log(person1.habit) // Games
console.log(person1.strength) // 60
person.sayName(); // I am Kevin
第二步,返回值的效果
- 情况1,构造函数的返回值是对象形式,只能访问返回的对象中的属性
- 情况2,返回值为基本类型,与没有返回值一样,正常使用原型对象属性和构造函数属性
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;
}
2、bind模拟实现
bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的this,之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )
var foo = {
value: 1
};
function bar() {
console.log(this.value, arguments);
}
// 返回了一个函数
var bindFoo = bar.bind(foo, 'aaa');
bindFoo('bbb'); // 1 Argument['aa','bbb']
bind的作用:
- 1、返回新的函数
- 2、this指向第一个参数
- 3、传递参数
第一步,返回新的函数
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 值会失效,但传入的参数依然生效。
var foo = {
value: 1
};
function bar(name, age) {
this.habit = 'shopping';
console.log(this.value, name, age);
}
bar.prototype.friend = 'kevin';
var bindFoo = bar.bind(foo, 'daisy');
var obj = new bindFoo('18');
console.log(obj.habit);
console.log(obj.friend);
//undefined 'daisy' '1'
//undefiend
//undefined
Function.prototype.bind1 = function(context){
let self = this;
let args1 = Array.prototype.slice.call(arguments, 1);
let func = function (){
let args2 = Array.prototype.slice.call(arguments);
//作为普通函数调用时,this为window
//作为构造函数调用时,this为func
return self.apply(this instanceof func ? this: context, args1.concat(args2));
}
func.prototype = this.prototype;
return func;
}
第四步,构造函数的效果优化
但是在这个写法中,我们直接将func.prototype=this.prototype,我们直接修改 func.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 args1 = Array.prototype.slice.call(arguments, 1);
var TempFunc = function () {};
var func = function () {
var args2 = Array.prototype.slice.call(arguments);
//作为普通函数调用时,this为window
//作为构造函数调用时,this为func
return self.apply(this instanceof TempFunc ? this : context, args.concat(args2));
}
TempFunc.prototype = this.prototype;
func.prototype = new TempFunc();
return func;
}
Object.create = function(o) {
function TempFunc(){}
TempFunc.prototype = o;
return new TempFunc();
};
注意:尽管在全局和 foo 中都声明了 value 值,最后依然返回了 undefind,说明绑定的 this 失效了,经过new 操作符,这个时候的 this 已经指向了 obj。
var value = 2;
var foo = {
value: 1
};
function bar(name, age) {
this.habit = 'shopping';
console.log(this.value, name, age);
}
bar.prototype.friend = 'kevin';
var bindFoo = bar.bind(foo, 'daisy');
//bindFoo('18')
var obj = new bindFoo('18');
console.log(obj.habit);
console.log(obj.friend);
//undefined 'daisy' '1'
//shopping
//kevin
3、call的模拟实现
call() 方法在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法。
- 1、改变this指向
- 2、调用函数
- 3、有返回值,context为null或window
第一步,改变this指向
模拟的步骤可以分为:
- 1)将函数设为对象的属性
- 2)执行该函数
- 3)删除该函数
Function.prototype.call = 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.call = 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 +')'); //字符串+数组,数组将会转成args.join()
delete context.__fn;
return result;
}
var foo = {
value: 1,
};
var value = 2;
function bar(a, b){
console.log(this.value, a ,b)
}
bar.call(null, 2, 3);
4、apply的模拟实现
类似于call,只是apply参数为数组
Function.prototype.apply = 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}]`);
}
//["arr[0]", "arr[1]", "arr[2]"]
result = eval('context.__fn(' + args + ')'); //字符串+数组,数组将会转成args.join()
}
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