对象的原型
JavaScript当中每个对象都有一个特殊的内置属性
[[prototype]]
,这个特殊的对象可以指向另外一个对象。
那么这个对象有什么用呢?
- 当我们通过引用对象的属性key来获取一个value时,它会触发
[[Get]]
的操作; - 这个操作会首先检查该属性是否有对应的属性,如果有的话就使用它
- 如果对象中没有改属性,那么会访问对象
[[prototype]]
内置属性指向的对象上的属性;
代码示例:
// 2.原型有什么用呢?
// 当我们从一个对象中获取某一个属性时, 它会触发` [[get]] `操作
// 1. 在当前对象中去查找对应的属性, 如果找到就直接使用
// 2. 如果没有找到, 那么会沿着它的原型去查找 `[[prototype]]`
//obj.age = 18
var obj = { name: "zayyo" }
obj.__proto__.age = 18
console.log(obj.age)
那么如果通过字面量直接创建一个对象,这个对象也会有这样的属性吗?如果有,应该如何获取这个属性呢?
答案是有的,只要是对象都会有这样的一个内置属性;
获取的方式有两种
- 通过对象的 proto 属性可以获取到(但是这个是早期浏览器自己添加的,存在一定的兼容性问题)
- 通过 Object.getPrototypeOf 方法可以获取到
代码示例:
// 我们每个对象中都有一个 [[prototype]], 这个属性可以称之为对象的原型(隐式原型)
var obj = { name: "zayyo" } // [[prototype]]
var info = {} // [[prototype]]
// 1.解释原型的概念和查看一下原型
// 早期的ECMA是没有规范如何去查看 [[prototype]]
// 给对象中提供了一个属性, 可以让我们查看一下这个原型对象(浏览器提供)
// __proto__
console.log(obj.__proto__) // {}
console.log(info.__proto__) // {}
// 原理
var obj = {name: "zayyo", __proto__: {} }
// // ES5之后提供的Object.getPrototypeOf
console.log(Object.getPrototypeOf(obj))
既然字面量创建的对象会有原型,那么创建的函数也会有原型吗?
答案是肯定的,接下来我们就详细讲解一下函数的原型。
函数的原型 prototype
所有的函数都有一个prototype的属性
你可能会问题,是不是因为函数是一个对象,所以它有prototype的属性呢?
不是的,因为它是一个函数,才有了这个特殊的属性。而不是它是一个对象,所以有这个特殊的属性。
代码示例:
function foo() {
}
// 函数也有原型(隐式原型)
console.log(foo.__proto__) // 函数作为对象来说, 它也是有[[prototype]] 隐式原型
// 函数它因为是一个函数, 所以它还会多出来一个显示原型属性: prototype
console.log(foo.prototype)
在对函数使用new操作符时,new会执行的步骤是和上面一样的,但是我们要==注意==到的是,new在执行
在内存中创建一个新的对象(空对象),并且这个对象内部的[[prototype]]
属性会被赋值为该构造函数的prototype属性,这两步会对我们的函数产生巨大的影响,
- 首先这意味着我们会有一个新的对象,
- 第二我们通过Person构造函数创建出来的所有对象的
[[prototype]]
属性都指向Person.prototype
代码示例:
function Person() {
}
var p1 = new Person()
var p2 = new Person()
// 都是为true
// new出来的对象的隐性原型(__proto__)会指向构造出这个对象的类的显示原型(prototype)
console.log(p1.__proto__ === Person.prototype)
console.log(p2.__proto__ === Person.prototype)
// p1.name = "zayyo"
//给构造原型添加一个name属性赋值为kobe
// p1.__proto__.name = "kobe"
// 修改了构造原型的name属性的值,变为james
// Person.prototype.name = "james"
// 修改了构造原型的name属性的值,变为zayyo
p2.__proto__.name = "zayyo"
console.log(p1.name)
constructor属性
事实上原型对象上面是有一个属性的:constructor,默认情况下原型上都会添加一个属性叫做constructor,这个constructor指向当前的函数对象。
function foo () {
}
// 1.constructor属性
// foo.prototype这个对象中有一个constructor属性
//查看prototyp属性
console.log(foo.prototype)
//查看constructor属性
console.log(Object.getOwnPropertyDescriptors(foo.prototype))
// 定义constructor
Object.defineProperty(foo.prototype, "constructor", {
enumerable: true,
configurable: true,
writable: true,
value: "哈哈哈哈"
})
// console.log(foo.prototype)
// prototype.constructor = 构造函数本身
console.log(foo.prototype.constructor) // [Function: foo]
console.log(foo.prototype.constructor.name)
console.log(foo.prototype.constructor.prototype.constructor.prototype.constructor)
// 2.我们也可以添加自己的属性
foo.prototype.name = "zayyo"
foo.prototype.age = 18
foo.prototype.height = 18
foo.prototype.eating = function () {
}
var f1 = new foo()
console.log(f1.name, f1.age)
如果我们需要在原型上添加过多的属性,通常我们会重新整个原型对象。前面我们说过, 每创建一个函数, 就会同时创建它的prototype对象, 这个对象也会自动获取constructor属性。而我们这里相当于给prototype重新赋值了一个对象, 那么这个新对象的constructor属性, 会指向Object构造函
数, 而不是Person构造函数了
// 3.直接修改整个prototype对象
foo.prototype = {
// constructor: foo,
name: "zayyo",
age: 18,
height: 1.88
}
var f1 = new foo()
console.log(f1.name, f1.age, f1.height)
// 真实开发中我们可以通过Object.defineProperty方式添加constructor
Object.defineProperty(foo.prototype, "constructor", {
enumerable: false,
configurable: true,
writable: true,
value: foo
})
如果希望constructor指向Person,那么可以手动添加,上面的方式虽然可以, 但是也会造成constructor的[[Enumerable]]
特性被设置了true。默认情况下, 原生的constructor属性是不可枚举的,如果希望解决这个问题, 就可以使用我们前面介绍的Object.defineProperty()函数了。我们在上一个构造函数的方式创建对象时,有一个弊端:会创建出重复的函数,比如running、eating这些函数。
那么有没有办法让所有的对象去共享这些函数呢?
有,将这些函数放到Person.prototype的对象上即可
代码示例:
// 变量属性要添加再构造类的函数里面,不可以添加剂再顶层原型里面,不然会造成数据被重复修改
function Person(name, age, height, address) {
this.name = name
this.age = age
this.height = height
this.address = address
}
// 而函数属性可以添加再顶层函数里面,不会造成重复修改,因为函数不会保存值,每次都会释放内存
Person.prototype.eating = function() {
console.log(this.name + "在吃东西~")
}
Person.prototype.running = function() {
console.log(this.name + "在跑步~")
}
var p1 = new Person("zayyo", 18, 1.88, "北京市")
var p2 = new Person("kobe", 20, 1.98, "洛杉矶市")
p1.eating()
p2.eating()
原文链接:https://juejin.cn/post/7229204793617530936 作者:zayyo