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 Monicanew操作符帮我们做了哪些事呢 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结果是一样的
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' }跟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 }明天更。。。