Chapter05----面向对象编程(续)
一、理解对象
二、理解原型
1.原型与构造函数、对象之间的关系
-
无论何时,只要创建一个函数,就会按照特定的规则为这个函数创建一个
prototype
属性(指向原型对象)。 -
原型对象会自动获取一个名为
constructor
的属性,指向与之关联的构造函数。例如Person.prototype.constructor指向Person。 -
在自定义构造函数时,原型对象默认只会获得constructor属性,其它的所有方法都继承自Object。
-
构造函数有一个prototype属性引用其原型对象,而这个原型对象也有一个constructor属性引用这个构造函数,两者循环引用。
function Person(){} console.log(typeof Person.prototype); console.log(Person.prototype.constructor === Person);//true //Object原型的原型是null console.log(Object.prototype.__proto__ === null);//true //正常的原型链都会终止与Object console.log(Person.prototype.__proto__ === Object.prototype);//true //实例通过__proto__链接到原型对象 const person = new Person(); console.log(person.__proto__.constructor === Person);//true //构造函数通过prototype链接到原型对象 console.log(Person.prototype === person.__proto__);//true //同一个构造函数创建的多个实例共享同一个原型对象 const p1 = new Person(); const p2 = new Person(); console.log(p1.__proto__ === p2.__proto__);//true console.log(person.__proto__ ==p1.__proto__);//true //构造函数的原型与其实例的原型对象是同一个 console.log(p1.__proto__ === Person.prototype);//true
2.原型的相关方法
isPrototypeOf()
:确定两个对象之间的原型关系。//isPrototypeOf() console.log(Person.prototype.isPrototypeOf(p1));//true
Object.getPrototypeOf()
:返回原型对象。//Object.getPrototypeOf() console.log(Object.getPrototypeOf(p1) === Person.prototype);//true
3.原型层级
- 在访问对象的属性和方法是会按照原型层级(原型链)来搜索==。例如,访问对象p的属name,如果该对象自身有该属性,则返回该属性值,否则会去搜索p的原型对象q是否包含该属性,依此不断搜索,直到搜索Object的原型对象中是否包含有该属性。
//原型层级(原型链)---访问属性 function Dog(){} Dog.prototype.name = "小灰"; const dog1 = new Dog(); const dog2 = new Dog(); dog1.name = "小黄"; console.log(dog1.__proto__ === dog2.__proto__);//true console.log(dog1.name);//小黄,来自实例dog1 console.log(dog2.name);//小灰,来自原型对象
hasOwnProperty()
:确定某个属性是否来自实例或者来自原型。//hasOwnProperty() console.log(dog1.hasOwnProperty("name"));//true console.log(dog2.hasOwnProperty("name"));//false
Object.getOwnPropertyNames()
:获取对象的所有属性名(无论是否可以枚举)。//Object.getOwnPropertyNames() console.log(Object.getOwnPropertyNames(dog1));//['name'] console.log(Object.getOwnPropertyNames(dog1.__proto__));//["constructor", "name"] console.log(Object.getOwnPropertyNames(Object));
- 注意:Object.getOwnPropertyDescriptor()方法只对实例对象有效。要得到原型的属性描述符,必须在原型对象上使用该方法。
4.原型和in操作符:
in
:
该操作符可以确定某个属性是否在实例中或在实例的原型中,在就返回true;否则返回false。//in操作符 console.log("name" in dog1);//true,name来自实例 console.log("name" in dog2);//true,name来自原型 //通过hasOwnProperty()和in操作符确定属性是否存在于原型中 console.log(!dog2.hasOwnProperty("name") && ("name" in dog2));//true
for-in
:
在循环中使用,循环迭代对象属性(可枚举属性)。//for-in for(const property in dog1){ console.log(property); }
属性枚举
:
1.for-in、Object.keys()这两个方式枚举顺序是不确定的。
2.Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()、Object.assign()枚举的顺序是确定的。对象迭代
:
Object.keys()
、Object.values()
、Object.entries()
//对象迭代 const o = { name:'bar', baz:"1", arr:[1, 2, 3], obj:{} }; //Object.keys() for(const key of Object.keys(o)){ console.log(key); } //Object.values() for(const val of Object.values(o)){ console.log(val); } //Object.entries() for(const [key, val] of Object.entries(o)){ console.log(key, val); }
三、创建对象
1.工厂模式
该模式的不足就是没有解决新创建的对象是什么类型。
//示例1---工厂模式创建对象
function createObject(name, age, gender){
const o = {};
o.name = name;
o.age = age;
o.gender = gender;
o.sayHello = function(){
console.log(`"Hello, I'm ${this.name}"`);
}
return o;
}
const obj1 = createObject("Lili", 23, "女");
console.log(obj1);
obj1.sayHello();
2.构造函数模式
- 通过使用
new操作符加构造函数创建对象
。使用new 后,会进行以下几个操作:
1.在内存中创建一个新对象。
2.这个新对象的[[Prototype]]
特性被赋值为构造函数的prototype
属性。
3.构造函数的this指向这个新对象。
4.执行构造函数内部的代码。
5.如果构造函数返回非空对象,则返回该对象;否则返回这个新对象。//示例2---构造函数创建对象 function Person(name, age, gender) { this.name = name; this.age = age; this.gender = gender; this.sayHello = function(){ console.log(`"Hello, I'm ${this.name}"`); } } const obj2 = new Person("Liling",24,"男"); const obj3 = new Person("Liming",24,"男"); console.log(obj2); console.log(obj2.sayHello === obj2.sayHello);//false obj2.sayHello();
- 构造函数的问题:
定义在构造函数内部的方法没有被它的每个实例所共享,而是没创建一个实例都会为其创建相应的方法。//示例2---构造函数创建对象(改进) function Person(name, age, gender) { this.name = name; this.age = age; this.gender = gender; this.sayHello = sayHello; } function sayHello(){ console.log(`"Hello, I'm ${this.name}"`); } const obj2 = new Person("Liling",24,"男"); const obj3 = new Person("Liming",24,"男"); console.log(obj2); console.log(obj2.sayHello === obj2.sayHello);//true obj2.sayHello();
3.原型模式
- 主要是利用函数的prototype来共享方法和属性。
//示例3---原型模式 function Dog(){} Dog.prototype.eat = function() { console.log("eating bone!!"); }; Dog.prototype.type = "中华田园犬"; const dog1 = new Dog("小灰","grey"); const dog2 = new Dog("小黄","yellow"); console.log(dog2.eat === dog1.eat);//true console.log(dog1.eat());
- 原型模式的问题是它弱化了构造函数传递参数的能力。
4.组合模式
一般是混合构造函数和原型两种模式来创建对象,即私有属性在构造函数中来定义,共享属性和方法在原型中定义。
//组合模式---构造函数模式和原型模式的组合
function Cat(name, color,type){
this.name = name;
this.color = color;
this.type = type;
}
Cat.prototype.eat = function(){
console.log("eats fish!");
}
const cat1 = new Cat("喵喵", "grey", 1);
const cat2 = new Cat("小喵", "yellow", 2);
console.log(cat1.__proto__ === cat2.__proto__);//true
cat1.eat();