原型?一次弄懂
原型是什么
所有
引用类型
都有一个__proto__(隐式原型)
属性,属性值是一个普通的对象
const arr = [] console.log(typeof arr.__proto__); // object 复制代码
所有
引用类型
的__proto__
属性指向
它构造函数的prototype
console.log(arr.__proto__ === Array.prototype); // true 复制代码
当一个对象在查找一个属性的时候,自身没有就会根据__proto__向它的原型进行查找,终点是
Object.prototype.__proto__
为null
,查找的过程形成了原型链
console.log(arr.toString); // [Function: toString] 复制代码
探索原型链
小伙伴们可以移步到JavaScript 世界万物诞生记,一篇帮助理解原型链的好文????
下面请出万恶之源
为什么需要原型和原型链
function Person(name) { this.name = name; this.sayname = function() { console.log(name); } } let p1 = new Person('xguo'); let p2 = new Person('xguo'); console.log(p1.sayname === p2.sayname); // false 复制代码
用构造函数调用生成实例对象,有一个缺点,无法共享属性和方法。每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费,考虑到这些,原型和原型链诞生了
function Person(name) { this.name = name; } Person.prototype.sayname = function() { console.log('----'); } let p1 = new Person('xguo'); let p2 = new Person('xguo'); console.log(p1.sayname === p2.sayname); // true 复制代码
如何理解原型链
我们知道Object.prototype.__proto__ === null
,为什么呢?因为JS从设计之初就想用null
表示空对象,最直接的证据:typeof null === 'object'
;因此ES5中用Object.create(null)
来生成没有原型的空对象
console.log(Object.prototype.__proto__ == Object.create(null).__proto__); // true 复制代码
也就是说,Object.prototype
是使用这种方式生成的,然后才绑在 Object
的 prototype
属性上,因为对象可以先生成再赋予新属性
console.log(Function.__proto__=== Function.prototype); // true 复制代码
Function
是一个函数,只要是函数,它的__proto__
就指向 Funtion.prototype
这是继承的设定。Function
生成时,是没有__proto__
属性的,它是一个BOM
对象,Function
的__proto__
和prototype
属性都是后面才指向同一个 BOM
对象的
console.log(Object instanceof Function) // true console.log(Object.__proto__=== Function.prototype); // true 复制代码
Object
是构造器函数, 因此它的__proto__
属性会指向Funtion.prototype
function Person() {} console.log(typeof Person.prototype); console.log(typeof Function.prototype); 复制代码
Function.prototype
是一个函数,也是对象,只有Function
的prototype
是函数其他都是普通对象。它是一个桥梁,我们一直说函数也是对象,只有这个满足了, 函数才能是对象
“构造函数” new
function Cat(name, color) { // 像其他语言的封装性 降低其他语言开发者的门槛 this.name = name this.color = color } var cat1 = new Cat('狗蛋', '白色') console.log(cat1.constructor === Cat); // true 复制代码
我们使用new
关键字,看起来是执行了类的构造函数方法,Cat
的首字母也大写了,并且cat1.constructor === Cat
,一切都好像在提示它是一个类
构造函数还是调用
实际上Cat
和程序中的其他函数没有任何区别,幕后黑手是new
,它会劫持所有普通函数并用构造对象的形式调用它。换句话说,在JavaScript中对于“构造函数”最准确的解释是,所有带new
的函数调用
.constructor属性
console.log(cat1.constructor === Cat.prototype.constructor); // true 复制代码
实际上,Cat.prototype.constructor
默认指向Cat,这和“构造”毫无关系。
function Cat() {} Cat.prototype = {} var cat1 = new Cat() console.log(cat1.constructor === Cat); // false console.log(cat1.constructor === Object); // true 复制代码
.constructor
属性只是Cat
函数在声明时的默认属性,如果创建一个新对象并替换函数默认的.prototype
引用,那么新对象并不会自动获得.constructor
属性
手动实现简版new
function Person(name, age, sex="女") { // console.log(arguments) this.name = name; this.age = age; this.sex = sex; } // 实例除了属性各有各的(私有的 constructor)通过prototype // 对象间共享的方法(prototype ) // construtor 完成对象构造,再使用prototype连接共享方法 Person.prototype.sayHi = function() { console.log('你好啊'); } // 手动实现new const objectFactory = function() { var obj = new Object(); // [].shift [] 对象 shift 上的方法 // this -> [] call -> arguments // console.log(Array.from(arguments).shift()); var varConstructor = [].shift.call(arguments); varConstructor.call(obj, ...arguments); obj.__proto__ = varConstructor.prototype; return obj; } const obj = objectFactory(Person, 'xguo', 19, '男'); console.log(obj); // Person {name: 'xguo', age: 19, sex: '男'} obj.sayHi(); // 你好啊
作者:onepiece1205
链接:https://juejin.cn/post/7015506102364307492