还搞不懂对象-函数-原型之间的关系?

70 阅读3分钟

一、原型和原型链的概念:

原型:是构造函数制造出的对象(构造出对象的祖先)的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。

原型链:当我们访问一个对象身上的属性时,如果他没有这个属性,他就会在prototype上找,prototype身上又会有自己的prototype,他会一直这样一层一层往上找下去。

Person.prototype.lastName = "hehe";   // 属性要小驼峰式,一般约定俗成
Person.prototype.say = function(){
     console.log('hehe');
}
function  Person(name, age, sex){      // 构造函数必需是大驼峰式,一般约定俗成
     //this.lastName = "xie";     //自己身上有,取自己身上的,不取原型
     this.name = name;
     this.age = age;
     this.sex = sex;
}
var person = new Person('caixukun', 35, 'male');
console.log(person);  // {name: 'caixukun', age: 35, sex: 'male'}
console.log(person.lastName) // hehe 通过原型链继承祖先的属性和方法

// 补充:
// Object.getOwnPropertyNames()  // 返回所有属性(包括可枚举和不可枚举),不可遍历出原型的属性
// Object.keys() // 返回所有可枚举的属性,不可遍历出原型的属性
// for...in...  // 返回所有可枚举的属性,可遍历出原型属性 


Object.defineProperty(person, 'address', {
    enumerable: false,
    value: 'shenzhen'
})
let arr = []
for(let key in person) {
     arr.push(key)
}
// 原型上的属性和方法也遍历出来了
console.log(arr) // [ 'name', 'age', 'sex', 'lastName', 'say' ]

// 只是遍历对象本身的属性和方法
console.log(Object.keys(person))  // [ 'name', 'age', 'sex' ] 返回所有可枚举的属性。
console.log(Object.getOwnPropertyNames(person))  // [ 'name', 'age', 'sex', 'address' ] 返回所有属性(包括可枚举和不可枚举)

原型也是对象

Car.prototype = {    //原型也是对象,所以可以这样
     height : 1400,
     lang : 4900,
     carName : "BMW"
}
function Car(){
}
var car = new Car()

constructor,构造的意思

function Car(){

}
var car = new Car()
console.log(car.constructor) // Car() 返回对象的构造函数,然而构造函数是空对象,构造函数没有哪来的constructor?看下一条
console.log(Car.prototype) // {} 【但是原型上隐式绑定constructor,指向构造函数本身,重点】
// console.log(car.prototype)  // undifined  car是个对象,不是构造函数,所以这里为undifined
// console.log(Car.prototype == car.__proto__) // true

// constructor 可以手动更改
function Person(){
     this.name = 'cai',
     this.sex = 'male',
     this.age = '18'
}
Car.prototype = {
     constructor : Person  // constructor是原型上的隐式属性
}
function Car(){
     this.name = 'BMW'
}
var car = new Car();
console.log(car.constructor); // Person()
console.log(car); // { name: 'BMW' }
console.log(car.sex); // undefined

原型题

1、改名

Person.prototype.name = 'sunny'
function Person () {}
var person = new Person ()
Person.prototype.name = 'cherry'
console.log(person.name); // cherry
Person.prototype.name = 'sunny'
function Person () {}
Person.prototype.name = 'cherry'
var person = new Person ()
console.log(person.name) // cherry

上面两个是直接在原先的改属性,改的是名,下面的是换了个空间,是原始值要区分开来

2、换空间

//这是sunny,先new 再生成新空间
Person.prototype.name = 'sunny';
function Person () {

}
var person = new Person ();
Person.prototype = {
     name : 'cherry'
}
console.log(person.name); // sunny

二、对象-函数-原型之间的关系

先把经典图贴上,是不是就有点慌?别急要想理解这张图我们只需知道几个前提条件。

原型.png

首先我们需要知道__proto__是构造函数构造出来对象的隐式原型,而prototype是构造函数的显式原型,构造函数制造出的对象的隐式原型等于构造函数的显式原型。 由以上可以知道:

var obj = {} // 本质是由new Object()构造出来的
console.log(obj.__proto__ === Object.prototype) // true
function Foo() {}
var foo = new Foo()
console.log(foo.__proto__ === Foo.prototype) // true
// 同时由上文的原型也是对象同样可以知道
console.log(Foo.prototype.__proto__ === Object.prototype) // true

其次, 在不改变constructor的前提条件下,构造函数的原型的constructor属性指向构造函数本身。 我们可以得出结论

function Foo() {}
function Function() {}
function Object() {}
console.log(Foo.prototype.constructor) // Foo()
console.log(Function.prototype.constructor) // Function()
console.log(Object.prototype.constructor) // Object()

最后,一切函数都可以认为是由function Function() {} 创建出来的,因此我们可以得出重要结论。

// Foo函数其实也是相当于new Function() 构建出来的
function Foo() {}
console.log(Foo.__proto__ === Function.prototype) // true
// 因为Object本身是一个函数,也是一个对象。再由第一点的构造函数制造出的对象的隐式原型等于构造函数的显式原型。所以
console.log(Object.__proto__  === Function.prototype) // true
// 同理Function本身是一个函数,也是一个对象。所以
console.log(Function.__proto__ === Function.prototype) // true

好了,最后让我们做三道题理解一下,是不是学废了。

function Foo() {}

console.log(Foo.prototype === Foo.__proto__) 

console.log(Foo.prototype.constructor)
console.log(Foo.__proto__.constructor)

// 以上答案为
// 1、false
// Foo.prototype的显式原型是一个空对象,Foo.__proto__的隐式原型指向Function.prototype,所以是false

// 2、Foo()
// 构造函数的原型的constructor属性指向构造函数本身。即:Foo()

// 3、Function()
// 因为Foo.__proto__ == Function.prototype,所以可知Foo.__proto__.constructor == Function.prototype.constructor, 再由构造函数的原型的constructor属性指向构造函数本身。即: Function()