手写 call、apply 及 bind 函数
javascript
Function.prototype.Dcall = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
var context = context || window
context.say = this
var args = []
for (let i = 1; i < arguments.length; i++) {
args.push('arguments[' + i + ']')
}
var result = eval('context.say(' + args + ')')
delete context.say
return result
}
javascript
Function.prototype.Dapply = function (context, arr) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
var context = context || window
var result
context.fn = this
if (!arr) {
result = context.fn()
} else {
var args = []
for (let i = 0; i < arr.length; i++) {
args.push('arr[' + i + ']')
}
result = eval('context.fn(' + args + ')')
}
return result
}
javascript
Function.prototype.Dbind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
var self = this
var args = Array.prototype.slice.call(arguments, 1)
return function () {
var bindArgs = Array.prototype.slice.call(arguments)
return self.apply(context, args.concat(bindArgs))
}
}
javascript
// 示例函数
function greet() {
console.log(`Hello, ${this.name}!`)
}
// 1. 使用 Dcall 方法调用函数,并指定上下文对象和参数
const person1 = { name: 'Alice' }
greet.Dcall(person1) // 输出:Hello, Alice!
// 2. 使用 Dapply 方法调用函数,并指定上下文对象和参数数组
const person2 = { name: 'Bob' }
const args = ['Nice to meet you!']
greet.Dapply(person2, args) // 输出:Hello, Bob!
// 3. 使用 Dbind 方法创建新函数,预先绑定上下文和参数
const person3 = { name: 'Charlie' }
const greetPerson3 = greet.Dbind(person3)
greetPerson3() // 输出:Hello, Charlie!
new
在调用 new 的过程中会发生以上四件事情:
- 新生成了一个对象
- 链接到原型
- 绑定
this
- 返回新对象
实现分析:
- 创建一个空对象
- 获取构造函数
- 设置空对象的原型
- 绑定
this
并执行构造函数 - 确保返回值为对象
javascript
Function.prototype.Dnew = function (fn, ...args) {
var obj = Object.create(fn.prototype)
var ret = fn.apply(obj, args)
return ret instanceof Object ? ret : obj
}
// 或
Function.prototype.DDnew = function () {
var obj = {}
var Constructor = Array.prototype.shift.call(arguments)
obj.__proto__ = Constructor.prototype
var result = Constructor.call(obj, arguments)
return result instanceof Object ? result : obj
}
instanceof
实现原理
就是只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false,告诉我们左边变量并非是右边变量的实例。
javascript
{
function new_instance_of(leftVaule, rightVaule) {
let rightProto = rightVaule.prototype
leftVaule = leftVaule.__proto__
while (true) {
if (leftVaule === null) return false
if (leftVaule === rightProto) return true
leftVaule = leftVaule.__proto__
}
}
}
{
function new_instance_of(leftVaule, rightVaule) {
let proto = Object.getPrototypeOf(leftVaule)
while (true) {
if (proto == null) return false
if (proto === rightVaule.prototype) return true
proto = Object.getPrototypeOf(proto)
}
}
}
总结:
使用 typeof 来判断基本数据类型是 ok 的,不过需要注意当用 typeof 来判断 null 类型时的问题,如果想要判断一个对象的具体类型可以考虑用 instanceof,但是 instanceof 也可能判断不准确,比如一个数组,他可以被 instanceof 判断为 Object。所以我们要想比较准确的判断对象实例的类型时,可以采取 Object.prototype.toString.call() 方法