一、call原理
定义:使用一个指定的this值和单独给出的一个或者多个参数来调用一个函数
语法:function.call(thisArg, arg1, arg2, ...)
原理分析:其实就是将要调用的函数作为指定对象的一个属性,然后执行指定对象中的属性(方法),最后将该对象上的属性删除,将执行的结果返回。如果未指定对象(如:传入null、undefined),那默认就指向全局对象(window)
具体实现如下:
Function.prototype.call2 = function (context) {
context = context || window
context.fn = this // this就是要执行的函数
var args = []
for (var i = 1; i<arguments.length; i++) {
args.push('arguments['+i+']')
}
var result = eval('context.fn('+args+')')
delete context.fn
return result
}
二、apply原理
apply实现原理同上,唯一的区别就是,传入的参数是数组
Function.prototype.apply2 = function (context, arr) {
context = context || window
context.fn = this
var result
if (!arr) {
result = context.fn()
} else {
var args = []
for (var i = 0; i < arr.length; i++) {
args.push('arguments['+i+']')
}
result = eval('context.fn('+args+')')
}
delete context.fn
return result
}
三、bind原理
定义:bind方法会创建一个新的函数,在bind被调用时候,这个新的函数的this指向为bind的第一个参数,而其余参数将作为新函数的参数,供调用时使用
语法:function.bind(thisArg[, arg1[, arg2[, ...]]])
原理分析:
- bind方法会返回一个新的函数
- 新函数执行时,它的this指向bind函数的第一个参数
- 新函数执行时,会将bind函数除第一参数之外的其余参数,与调用新函数时传入的参数合并,供调用的时候使用
初步实现如下:
Function.prototype.bind2 = function (context) {
var fn = this
var arg = Array.prototype.slice.call(arguments, 1)
var fbound = function () {
var bindArg = Array.prototype.slice.call(arguments)
fn.apply(context, arg.concat(bindArg))
}
return fbound
}
bind还有一个特性,就是如果bind返回的函数(fbound)被当成构造函数使用,那么该函数内部(fbound)的this指向的是它的实例,而忽略bind传入的context
举个例子:
var value = 2
var foo = {
value: 1
}
function bar(name, age) {
this.habit = 'shopping'; // 因为此时this指向实例obj
console.log(this.value);
console.log(name);
console.log(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
// 18
// shopping
// kevin
最终实现如下:
Function.prototype.bind2 = function (context) {
var self = this
var args = Array.prototype.slice.call(arguments, 1) // 将类数组转换成数组
var fNOP = function () {}
var fbound = function () {
// 作为构造函数,this就是fbound的一个实例,而self指向绑定函数(构造函数)
// 作为普通函数,this指向window,self指向绑定函数
var bindArg = Array.prototype.slice.call(arguments)
return self.apply(this instanceof self ? this : context, args.concat(bindArg))
}
fNOP.prototype = self.prototype;
fbound.prototype = new fNOP()
// 这里不这样写的原因是,如果一旦改了fbound.prototype,那么绑定函数的原型也就被修改了
// fbound.prototype = self.prototype;
return fbound
}
四、new 原理
特性分析:
- new XXX()会返回一个新的对象(那不就是new一个空对象么 如:var obj = new Object())
- 新的对象能访问构造函数的属性(哦,那就是使用var result = XXX.apply(obj)这样obj就有构造函数中的属性了)
- 新的对象的能访问构造函数中原型属性(那不就是obj._proto_ = XXX.prototype么)
- 如果构造函数中返回的是一个对象,那么new的结果就是该对象(判断result结果)
举个例子,验证一下第4点
function Person () {
this.name = 'zbq'
this.age = 18
return {
name: 'lisi',
age: 12
}
}
var obj = new Person()
console.log(obj)
// { name: 'lisi', age: 12 }
具体实现如下:
function objectFactory() {
// 参数中第一个参数为构造函数
var Constructor = [].shift.call(arguments)
var obj = new Object()
obj.__proto__ = Constructor.prototype
var result = Constructor.apply(obj, arguments)
return typeof result === 'object' ? (result || obj) : obj // 由于typeof null => 'object',如果result为null,就返回obj
}
五、Object.create实现
定义:Object.create静态方法以一个现有对象作为原型,创建一个新对象
语法
Object.create(proto, propertiesObject)
参数分析:
proto:新创建对象的原型对象
propertiesObject(可选):如果该参数被指定且不为 [`undefined`]则该传入对象[可枚举的自有属性]将为新创建的对象添加具有对应属性名称的属性描述符。这些属性对应于 [`Object.defineProperties()`]的第二个参数。
Object.myCreate = function (proto, propertyObject = undefined) {
if (propertyObject === null) {
// 这里没有判断propertyObject是不是原始包装对象
throw 'TypeError'
} else {
function Fn () {}
Fn.prototype = proto
const obj = new Fn()
if (propertyObject !== undefined) {
Object.defineProperties(obj, propertyObject)
}
if (proto === null) {
// 建立一个没有原型对象的对象,Object.create(null)
obj.__proto__ = null
}
return obj
}
}