JavaScript模拟new、call、apply、bind实现

JavaScript模拟new、call、apply、bind的实现

1.new

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象类型之一

我们可以通过代码看看new到底做了哪些事
// 定义一个构造函数
// 人
function Person (name, age) {
	// 姓名
	this.name = name
	// 年龄
	this.age = age
}
// 身高
Person.prototype.height = '176`'
// 定义一个行为
Person.prototype.say = function () {
	console.log(`My name is ${this.name}`)
}
// 现在我们用new操作符
var person = new Person('Monica', '21')
console.log(person.name)   //Monica
console.log(person.age)    //21
console.log(person.height) //176
console.log(person.say())  //My name is Monica

new操作符帮我们做了哪些事呢
1.可以直接访问构造函数Person里面的属性
2.可以访问构造函数prototype中的属性
简单来说就是一句话:函数Person的prototype属性指向了Person创建的实例person的原型

Person.prototype === person.__proto__  // true

现在来模拟一下new的实现
构造函数Person不变,变得是不通过new如何拿到一样功能的person
把new功能的实现封装成一个函数factory

functon factory () {
	// 此时我们并不知道会传几个参数进来
	// 在JS中有一个arguments对象,恰好它消除了我们这个顾虑
	// 因为new操作符得到的是一个对象,所以我们需要新建一个对象用来当做返回值
	var obj = new Object()
	// arguments是一个类数组对象,我们需要取第一个参数出来也就是Person
	Constructor = [].shift.call(arguments)
	// 将obj原型指向构造函数
	obj.__proto__ = Constructor.prototype
	// 将构造函数的this指向绑定到obj  结果就是obj.name = this.name
	Constructor.apply(obj, arguments)
	return obj
}
// 第一个参数肯定是传一个构造函数进去
var person = factory(Person, 'Monica', '21')
console.log(person.name)   //Monica
console.log(person.age)    //21
console.log(person.height) //176
console.log(person.say())  //My name is Monica

结果是一样的

2.call

call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。

我们可以通过代码看看call到底做了哪些事
var obj = {
	name: 'Monica'
}
function inputName () {
	console.log(this.name)
}
inputName()         // undefined
inputName.call(obj) // Monica  注意一点inputName执行了

有什么变化呢:
1.第一次调用,首先它会去函数内部this上下文去寻找name,很显然没有找到,name它会继续向上查找,也就是window,此时this.name = window.name ,然而全局也没有name,所以name是一个undefined
2.第二次调用call操作符将函数内部this指向改变了,指向了obj,所以此时的this.name = obj.name,也就是’Monica’

简单来说就是this指向变了

现在来模拟一下call的实现
var obj = {
	name: 'Monica',
	fn: function () {
		console.log(this.name)
	}
}
obj.fn() // 'Monica'
// 虽然实现了,显然不符合规范多了一个inputName属性,其实删掉就好了
delete obj.inputName

此时思路就出来了
1.添加属性 obj.fn = inputName
2.调用函数 obj.fn()
3.删除属性 delete obj.fn()
再试试

// 定义一个原型方法,也就是说所有函数都能用
Function.prototype.newCall = function (context) {
	// 这一步操作其实是核心,现在我们把context看成上面的obj
	// 这里的this肯定是指向Function
	context.fn= this // 这一步也就是 obj.fn= this
	context.fn();
    delete context.fn;
}
bar.newCall(obj) // 'Monica'

这是没有参数的情况,完善一下,第一个参数为null且函数有返回值
也就是inputName.call(null, ‘Monica’, ‘21’)
实现一下

var age = '21'
var obj = {
	name: 'Monica'
}
function inputName (age) {
	return {
        name: this.name,
        age: age
    }
}
inputName.call(null) // '21'  this.指向window
Function.prototype.newCall = function (context) {
	var context = context || window
	context.fn = this
	var args = [...arguments] // 类数组对象转数组
	args.shift() // 去掉第一个参数
	var res = context.fn(args)
	delete context.fn
	return res
}
console.log(inputName.call(obj, '21'))
{ 
	name: 'Monica', 
	age: '21' 
}

3.apply

跟call用法类似,只不过参数是数组

Function.prototype.newApply = function (context, arr) {
	var context = context || window
	context.fn = this
	var res
	if (!arr) {
        res= context.fn();
    } else {
		var args = [...arr]
		args.shift() // 去掉第一个参数
		res = context.fn(args)
	}
	
	delete context.fn
	return res
}

4.bind

明天更。。。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值