1. call
传入任意个参数,立即执行
使用一个指定的 this 值和若干指定的参数值的前提下,调用某个函数或方法:
var foo = { value : 1}
function bar(){
console.log(this.value)
}
bar.call(foo)// 1
// call 改变了 this 的指向,指向了 foo;
// bar 函数执行了;
实现指定的 this 值,模拟的步骤分为:
- 将函数设为对象的属性;
- 执行该函数;
- 删除该函数;
Function.prototype.call = function(context){
// 获取调用 call 的函数,用 this 可以获取
console.log(this)// bar(){ console.log( this.value ) }
context.fn = this
context.fn()
delete context.fn
}
// 测试一下
var foo = { value : 1 }
function bar(){
console.log(this.value)
}
bar.call(foo)// 1,相当于foo.bar()
实现若干指定的参数值:
- 从 Arguments 对象中取值,取出第二个到最后一个参数,放到一个数组里
Function.prototype.call = function(context){
context.fn = this
var args = [...arguments].slice(1)
context.fn(...args)
delete context.fn
}
// 测试一下
var foo = { value : 1 }
function bar(name,age){
console.log(this.value)
console.log(name)
console.log(age)
}
bar.call(foo,'zouyan','18')
// 1
// zouyan
// 18
优化:
- this 参数可以传 null ,当为 null 时,视为指向 window;
- 函数可以有返回值;
Function.prototype.call = function(context) {
var context = context || window
context.fn = this
var args = [...arguments].slice(1)
var result = context.fn(...args)
delete context.fn
return result
}
// 测试一下
var value = 2
var foo ={ value : 1 }
function bar(name,age){
console.log(this.value)
return{
value: this.value,
name: name,
age:age
}
}
bar.call(null)// 2
bar.call(foo,'zouyan',18)// {value:1,name:'zouyan',age:'18'}
2. apply
传入参数数组,立即执行
Function.prototype.apply = function(context, arr) {
var context = Object(context) || window
context.fn = this
var result
if (!arr) {
result = context.fn()
} else {
var args = arr || []
result = context.fn(...args)
}
delete context.fn
return result
}
// 测试一下
var value = 2
var foo ={ value : 1 }
function bar(name,age){
console.log('value',this.value)
return{
value: this.value,
name: name,
age:age
}
}
bar.apply(null)
bar.apply(foo,['zouyan',18])// {value:1,name:'zouyan',age:'18'}
3. bind
传入参数,返回函数,需要手动执行
- bind() 方法会创建一个新函数;
- 传入 bind 的第一个参数作为它运行时的 this;
- 传入的其他参数作为函数运行时的参数;
- 两个特点:返回一个函数,可以传入参数;
// 模拟返回函数:
Function.prototype.bind = function(cxt){
var self = this
return function(){
return self.apply(cxt)
}
}
// 模拟传参
Function.prototype.bind = function(cxt){
var self = this
// 获取 bind 函数从第二个参数到最后一个参数
var args = [].slice.call(arguments, 1)
return function(){
// arguments 指 bind 返回的函数传入的参数
var newArgs = args.concat([].slice.call(arguments))
return self.apply(cxt, newArgs)
}
}
一个绑定函数 也能使用 new 操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
// 模拟实现构造函数
Function.prototype.bind = function(cxt){
var self = this
var args = [].slice.call(arguments, 1)
var fNOP = function(){}
var fBound = function(){
var newArgs = args.concat([].slice.call(arguments))
return self.apply(this instanceof fNOP ? this : cxt, newArgs)
}
fNOP.prototype = this.prototype
fBound.prototype = new fNOP()
return fBound
}
// 调用 bind 函数,不是函数则报错
// 兼容
Function.prototype.bind = function(cxt){
if(typeof this !== "function"){
throw new Error("is not a function")
}
var self = this
var args = [].slice.call(arguments, 1)// 获取第二个参数到最后一个参数
var fNOP = function(){}
var fBound = function(){
var newArgs = args.concat([].slice.call(arguments))
return self.apply(this instanceof fNOP ? this : cxt, newArgs)
}
fNOP.prototype = this.prototype
fBound.prototype = new fNOP()
return fBound
}
4. new
- 实例可以访问到构造函数里的属性;
- 实例可以访问到构造函数原型中的属性;
实现步骤:
- ① 建立一个新对象 obj :因为 new 的结果是一个新对象;
- ②
obj._proto_
属性指向构造函数的 prototype :因为实例需要访问原型上的属性; - ③ 使用 apply 为 obj 添加新的属性:因为 obj 具有构造函数里的属性;
- ④ 判断返回值是不是一个对象,是对象返回对象,否则返回 obj :构造函数可能有返回值;
// 模拟实现 new 关键字
function objectFactory() {
// 步骤 ①
var obj = new Object(),//存疑,为什么模拟new里面使用new?
//var obj = Object.create(null),//报错:TypeError
//var obj = {},//正常
//var obj = Object.create({}),//正常
//var obj = Object.create(Object.prototype),//正常
// 步骤 ②
// 获取构造函数,截取第一个参数;
// shift 会修改原数组,所以 arguments 会被去除的一个参数;
Constructor = [].shift.call(arguments);// 等同于 Array.prototype.shift.call(arguments)
obj.__proto__ = Constructor.prototype;
var ret = Constructor.apply(obj, arguments); // 步骤 ③
return typeof ret === 'object' ? ret||obj : 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
参考链接 :github.com/mqyqingfeng…