__proto__([[Prototype]])
和prototype
每个对象都有一个隐式原型,这个隐式原型可以通过
obj.__proto__
Object.getPrototypeOf(obj)
这两种方式获取;
我们都知道对象是通过构造函数构造的,new关键字构造的,
构造函数上有一个显式原型属性,对象的隐式原型就指向了它的构造函数的显式原型。
let obj1 = new Object()
console.log(obj1.__proto__ == Object.prototype)//true
而这个构造函数也是一个函数对象对不对?所以构造函数的隐式原型对象也是指向函数的显式原型对象的。
console.log(Object.__proto__ == Function.prototype)
例子:
//Person构造函数
function Person() {
}
//通过构造函数得到两个实例对象
let p1 = new Person()
let p2 = new Person()
console.log(p1.__proto__ == Person.prototype)//true
//Person也是一个函数对象,是new Function的语法糖
console.log(Person.__proto__ == Function.prototype)//true
所以,对象上只有隐式原型对象__proto__,而它的构造函数上因为既是函数又是函数对象,所以既有显式原型又有隐式原型对象,一个对象的隐式原型指向它的构造函数的显式原型。
constructor
属性指向当前的构造函数
console.log(p1.__proto__.constructor)
console.log(Person.prototype.constructor)
console.log(Function.prototype.constructor)
每个对象有一个隐式原型对象,用__proto__
代替。指向构造函数的显式原型对象,这个隐式原型对象也是一个对象对不对,所以隐式原型对象上也有__proto__
隐式原型对象,
所以当我们查找一个对象的key对应的属性值的时候,对象本身没找到,会查找对象的隐式原型,隐式原型的隐式原型对象……这样层层查找的轨迹就是原型链。原型链总有尽头,那尽头是什么呢?就是Object对象的隐式原型对象,而它指向null,所以在代码中我们查找到原型对象的值为null的时候说明查找到了尽头。
我们知道对象可以通过原型链查找属性与方法,那在原型链上重写的方法就会被所有通过原型链继承的对象共享。
主要原理是让父类的实例对象作为子类构造函数的显式原型对象,这个父类实例对象的隐式原型指向的是自身构造函数的显式原型对象,这个显式原型对象的隐式原型对象指向的Object的原型对象。
function Person() {
}
function Student() {
}
let p = new Person()
Student.prototype = p
let stu = new Student()
console.log(stu.__proto__ == Student.prototype)//true
console.log(Student.prototype == p)//true
console.log(stu.__proto__ == p)//true
console.log(p.__proto__ == Person.prototype)//true
console.log(Person.prototype.__proto__)//Object对象
console.log(Person.prototype.__proto__.__proto__)//null
//Object.create方法,使用现有对象作为对象的原型
let o = Object.create(p)
console.log(o.__proto__ == p)//true
instanceof:检测某个构造函数的显式原型prototype是否在某个实例对象的原型链上
isPropertyOf:检测某个对象是否在某个对象的原型链上。
for in:遍历对象及其原型上的属性
hasOwnProperty:是否是对象自身的属性
Object.defineProperty
class是构造函数和原型链的语法糖。所以操作一个类实际也就是操作本质上的构造函数。
//改变隐式原型是为了继承,改变this指向是为了为原型对象挂载属性;
新建一个空对象,这个对象其实就是原型对象;
将这个对象隐式原型对象被赋值为构造函数也就是类的显式原型对象;
将构造函数内部的this指向这个对象;
然后是执行构造函数内的代码,大致就是挂载属性与方法到这个空对象中;
如果构造函数有传入的对象返回那个对象,如果没有其他对象就返回这个空对象。
class Person {
constructor(name, age) {
//使用new关键字实例化类(实例化对象)的时候会自动调用这个函数
//在这个函数主要做的是在原型对象上绑定共享属性与方法
this.address = "南京"
this.sex = "女"
this.name = name
this.age = age
}
//setter、getter方法
set name(name) {
console.log("调用了setter方法")
this._name = name
}
get name() {
console.log("调用了getter方法")
return this._name
}
getAge() {
return "年龄是:" + this.age
}
//静态方法
static address() {
return new Person("syc", 70)
}
//extends和super
}
let p = new Person("yyy", 20)
console.log(p.name)//调用了setter和getter方法
console.log(Person.address())
class Student extends Person {
constructor(name, age, classes) {
//复用父类的构造函数需要super关键字
super(name, age)
this.classes = classes
}
getMessage() {
return this.name + ":" + this.address + ":" + this.age + ":" + this.classes
}
getAge() {
//调用父类方法需要用super
return super.getAge()
}
static Ok() {
//调用父类静态方法用super
return super.address()
}
}
let stu = new Student("zzz", 12, 1903)
console.log(stu.getMessage())
console.log(stu.getAge())
console.log(Student.Ok())
继承还可以继承内置类比如Array等,对其内置方法进行修改,方便我们使用内置类;