<script>
//前置知识
// 每一个函数在创建之初就会有一个prototype属性,这个属性指向函数的原型对象
// function abc(){
// }
// abc.prototype--> {constructor: f}
// 在JS中任意的对象都有内置的属性叫做[[prototype]]这是一个私有属性,这个私有属性通过__proto__的方法来访问。
// 隐式原型指向这个对象函数的显式原型
// function Abc(){
// this.name='wn'
// }
// let test = new Abc()
// test.__proto__=Abc.prototype
//实例的隐式原型等于它构造函数的显式原型
function Foo(e) {
this.name = 'wn'
this.age = e
//如果构造函数有个return,return出来的值会影响到我们的返回结果,如果return出来的是个对象的话 那我们的作用域会出现一些微妙的结果,return不是对象的话没什么影响 但是如果是对象的话return出来的值就会受到影响 是它本身
return {}
}
//手动实现new操作符
function objectFactory() {
// 1. 定义一个对象出来 console.log(new Foo(18)) new Foo一定是一个对象 实例对象实例对象
const obj = {}
//3. 构造函数可以接收参数的,但是要先拿到这个构造函数,shift删除数组的第一项并且返回该项值,把shift方法里面的作用域修改到arguments里面去,call方法改变this指向
//[].shift.call(arguments) 的目的是将 arguments 对象转换成数组,并调用数组对象的 shift 方法来取出第一个参数(构造函数)。由于 arguments 是类数组对象而不是真正的数组对象,所以不能直接调用 shift 方法。因此,我们借助 call 方法来显式指定调用 shift 方法时的上下文对象,也就是将 shift 方法中的 this 指向 arguments 对象,从而实现从 arguments 中取出第一个参数的目的。
const Constructor = [].shift.call(arguments)
//或者是下面这样的方法,解构赋值 ...arguments这里面可能有很多参数 但是第一个指定是constructor,这里传进来的参数其实是console.log(objectFactory(Foo,18))第一个传进来的肯定是Foo
//const [Constructor, ...args] = [...arguments]
//现在拿到了我们的构造函数需要调用一下,因为new的时候的实例对象会继承函数的属性和方法,所以接下来是要实现要将函数的属性或者方法添加到构造函数上去
//5. 执行我们的原型连接 ,
obj.__proto__ = Constructor.prototype
const ret = Constructor.apply(obj, arguments) //4.要将构造函数的作用域指到我们的obj的空对象作用域中来,arguements顺带捎上人家的参数
//return obj //2. 返回一个参数
return typeof ret === 'object' ? ret : obj
}
console.log(objectFactory(Foo, 18));
</script>
运行之后如图(构造函数没有return的时候):