理解javascript中的原型

原型模式

每个函数都会创建一个prototype属性,这个属性是一个对象,包含着由特定引用类型的实例共享的属性和方法,这个对象就是调用构造函数创建的对象的原型,好处就是,在它上面定义的属性和方法可以被对象实例共享,原来在构造函数中直接赋值给对象实例的值可以直接赋值给他们的原型。

function Person(){
    
    }
Person.prototype.name = '小白'
Person.prototype.age = 22
Person.prototype.job = '开发'
Person.prototype.sayName = function(){
    
     console.log(this.name)}

let person1 = new Person()
person1.sayName() //小白

let person2 = new Person()
person2.sayName() //小白

console.log(person1.sayName == person2.sayName) //true

/**
*   所有的属性和方法都被添加到Person构造函数的prototype属性中了
* 构造函数主体中什么都没有,使用这种方式定义的属性和方法是由所有的实例
* 共享的,因此person1和person2访问的都是相同的属性和sayName()函数
*/

理解原型

只要是创建一个函数,就会为根据特定的规则为这个函数创建prototype属性指向原型对象,原型对象又会有一个constructor属性,指向与之关联的构造函数,比如Person.prototype.constructor 指向的是 Person,因构造函数而异,也可能会给原型上创建其他属性和方法,自定义构造函数的时候,原型对象上的有些方法会继承自Object ,每次调用构造函数创建一个实例,会暴露一个__proto__属性,通过这个属性可以访问对象的原型,主要是一点,实例与构造函数原型之间有直接的联系,与构造函数之间没有直接的联系

/**
*  构造函数可以是函数声明也可以是表达式
*  function Person(){
    
    }
*  let Person = function(){
    
    }
*/
console.log(Person.prototype)
//{
    
    
//   constructor:f Person(),
//   __proto__:Object
// }

/**
*	构造函数有一个prototype属性引用原型对象
*	原型对象有一个constructor属性引用构造函数
*	两者循环引用
*/
console.log(Person.prototype.constructor === Person) //true

/**
* 正常的原型链都会终止于Object的原型对象,Object的原型的原型是null
*/
console.log(Person.prototype.__proto__ === Object.prototype) //true
console.log(Person.prototype.__proto__.constructor === Object); // true
console.log(Person.prototype.__proto__.__proto__ === null);

let person1 = new Person()
let person2 = new Person()

/**
* 构造函数  实例 原型对象 是三个不同的对象
*/
console.log(perosn1 === Person) //false
console.log(person1 === Person.prototype) //false
console.log(Person.prototype === Person) //false

/**
*  实例的__proto__属性指向构造函数的原型对象
*  构造函数通过prototype链接到原型对象
*/
console.log(person1.__proto__ === Person.prototype) //true
console.log(person1.__proto__.constructor === Person) //true 构造函数与实例之间没有直接的联系

/**
* 同一个构造函数的两个实例共享同一个原型对象
*/
console.log(person1.__proto__ === person2.__proto__) //true

关系图
Object.getPrototypeOf()可以返回传入对象的原型

console.log(Object.getPrototypeOf(person1) ==Person.prototype); //true
console.log(Object.getPrototypeOf(person1).name); //小白

Object 类型还有一个 setPrototypeOf() 方法,可以重写对象的原型继承关系

let newProto = {
    
    
	gender:"男"
}
let person = {
    
    
	name: '小明'
};
Object.setPrototypeOf(person, newProto);
console.log(person.name);// 小明
console.log(person.gender);// 男
console.log(Object.getPrototypeOf(person) ===newProto); // true

//也可以通过Object.create()创建一个对象并为其指定一个原型
let person = Object.create(newProto);
console.log(Object.getPrototypeOf(person) ===newProto); // true

原型层级

通过对象访问属性的时候,会先在对象的自身实例上开始搜索,如果没有找到的话就会沿着指针找到原型对象,找到了就返回,如果在实例上添加了与原型中同名的属性,就会遮住对象原型上的属性

function Person(){
    
    }
Person.prototype.name == '小白'
let person1 = new Person()
let person2 = new Person()
person1.name = '小红'
console.log(person1.name) //小红 来自实例
console.log(person2.name) //小白 来自原型

//在实例上找到这个属性之后就不会再搜索圆形对象,使用delete操作符可以删除实例上的属性
delete person1.name
console.log(person1.name) //小白 来自原型

hasOwnProperty() 方法用于确定某个属性是在实例上还是在原型对象上。在实例上就会返回true

person1.name = '小红'
console.log(person1.hasOwnProperty("name")); //true
console.log(person2.hasOwnProperty("name")); //false

in操作符 可以通过对象访问指定属性的时候返回true 无论属性在实例上还是在原型上

console.log(person2.hasOwnProperty("name")); //false
console.log('name' in person2); //true

如果要确定某个属性是否存在于原型上,则可以像下面这样同时使用 hasOwnProperty() 和 in操作符:

function hasPrototypeProperty(obj,name){
    
    
	return !obj.hasOwnProperty(name) && (name in obj)
}

要获得对象上可枚举的实例属性 Enumerable:true 可以使用Object.keys() 接受一个对象作为参数 返回这个对象上所有的可枚举的属性名称字符串组成的数组

function Person() {
    
    }
Person.prototype.name = '小白'
Person.prototype.age = 23
Person.prototype.job = '开发'
console.log(Object.keys(Person.prototype)) //['name','age','job']

如果想列出所有实例属性,无论是否可以枚举,都可以使用
Object.getOwnPropertyNames() :

Object.getOwnPropertyNames(Person.prototype)

猜你喜欢

转载自blog.csdn.net/qq_42736311/article/details/115409050
今日推荐