1、概念
对象:一组属性和方法的集合。JavaScript中,一切皆对象,由普通对象(Object)和函数对象(Function)组成。
原型:又称原型对象,在定义函数时被创建,是一组属性和方法的集合,是JavaScript面向对象实现的主要方式。
prototype:每个函数有prototype属性(指针),指向其原型对象;可见,普通对象没有prototype。
constructor:原型对象中的constructor属性(指针),指向其构造函数。
__proto__:用构造函数实例化一个对象,实例对象中含有__proto__属性,其指向原型对象。
原型链:JavaScript通过原型实现对象,在对象的构造函数和实例中都有相关的属性指向其原型对象;而原型对象同样也是对象,也存在同样的属性指向其原型对象,直至追溯到Object.prototype原型对象;按照这规律,形成原型链,当使用对象的某些属性或方法时,就沿着原型链上寻找,直接找到或返回空值(null或undefined)。
下面结合示例代码一起学习:
function Person() {
}
Person.prototype.fisrtName = "Li";
Person.prototype.lastName = "Bill";
Person.prototype.age = 12;
Person.prototype.sayHello = function() {
console.log("My name is " + this.fisrtName + " " + this.lastName);
}
var person1 = new Person();
var person2 = new Person();
person1.sayHello();
console.log(Person.prototype == person1.__proto__); // true
console.log(Person.prototype.constructor == Person); // true
console.log(Person.prototype.__proto__ == Object.prototype); // true
console.log(Object.prototype.constructor == Object); // true
console.log(Object.prototype.__proto__ == null); // true
console.log(person1.__proto__ == person2.__proto__); // true
console.log(person1.constructor == Person); // true
![](https://i-blog.csdnimg.cn/blog_migrate/4005cd7f390aec4868a0fe8c22ada0c9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2d6725a068f5fdff59db11a55bc615c0.png)
2、作用
让同一类型的所有对象实例共享属性和方法。
3、应用
(1) 对象实现
构造函数模式:定义实例属性和方法,根据参数差异化对象。
function Person(first, last, age) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.sayHello = function () {
console.log("My name is " + this.firstName +" "+ this.lastName);
}
}
var person1 = new Person("Li", "Bill", 18);
var person2 = new Person("Wang", "John", 19);
person1.sayHello();
person2.sayHello();
原型模式:定义对象的共享属性和方法。
function Person(first, last, age) {
this.firstName = first;
this.lastName = last;
this.age = age;
}
Person.prototype.job = "IT";
Person.prototype.sayHello = function () {
console.log("My name is " + this.firstName +" "+ this.lastName);
}
var person1 = new Person("Li", "Bill", 18);
var person2 = new Person("Wang", "John", 19);
person1.sayHello();
console.log("My job is = " + person1.job);
person2.sayHello();
console.log("My job is = " + person2.job);
组合构造函数模式和原型模式:同时定义差异和共性
function Person(first, last, age) {
this.firstName = first;
this.lastName = last;
this.age = age;
if (typeof this.sayHello != "function") {
Person.prototype.sayHello = function() {
console.log("My name is " + this.firstName + " " + this.lastName + ", " + this.age + " old.");
}
}
}
var person1 = new Person("Li", "Bill", 13);
var person2 = new Person("Wang", "John", 12);
person1.sayHello();
person2.sayHello();
字面量方式创建原型对象:Person原型(实例person)的默认constructor会改变,为Object();可以在字面量赋值上增加constructor的指向为Person,但此时constructor性质变为可枚举;person0为先创建的对象,其构造函数依然为Person(),其原型不带任何属性和方法。
function Person() {
}
var person0 = Person();
Person.prototype = {
// without constructor point to
//constructor: Person, // change default [[Enumerable]] true
firstName: "Wang",
lastName: "Bill",
age: 12,
sayHello: function() {
console.log("My name is " + this.firstName + " " + this.lastName);
}
}
console.log(Person.prototype);
console.log(Person.prototype.constructor); // [Function: Object]
var person = new Person();
person.sayHello();
console.log(person.constructor); // [Function: Object]
console.log(person.__proto__);
也可以用如下方式重新定义constructor:
// reset contructor
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
value: Person
});
寄生构造函数模式:这种方式相当于把构造函数的作用变为一般函数,返回了一个Object实例。
function Person(first, last, age) {
var o = new Object();
o.firstName = first;
o.lastName = last;
o.age = age;
o.sayHello = function() {
console.log("My name is " + this.firstName + " " + this.lastName);
};
return o;
}
var person1 = new Person("Wu", "Kate", 12);
person1.sayHello();
Object.create( )创建对象方式:用一个对象作为新对象的原型。
function Person() {
this.sayName = function() {
console.log("My name is Tom Bruce");
}
}
var person = new Person();
var person1 = Object.create(person);
person1.sayName();
console.log(person1.__proto__ == person); // true
(2)常用API
确认对象是否为某一构造函数的原型类型:isPrototypeOf( );
获取对象的原型对象:Object.getPrototypeOf( )。
function Person(first, last, age) {
this.firstName = first;
this.lastName = last;
this.age = age;
}
Person.prototype.name = "Wang Bill"
var person1 = new Person("Wang", "Bill", 18);
console.log(Person.prototype.isPrototypeOf(person1));
console.log(Object.getPrototypeOf(person1).name);
console.log(Person.prototype.name);
判断对象是否含有某个属性:hasOwnProperty( );
判断一个属性是否存在于某个对象或原型:in 操作符;
结合上面的判断,可以判断属性是否存在于某个原型。
function Person() {
this.firstName = "Li";
}
Person.prototype.firstName = "Wang";
Person.prototype.lastName = "Bill";
Person.prototype.sayName = function() {
console.log("My name is " + this.firstName + " " + this.lastName);
}
var person1 = new Person();
console.log(person1.hasOwnProperty("firstName")); // true
delete person1.firstName;
console.log(person1.hasOwnProperty("firstName")); // false, instance has deleted
console.log("firstName" in person1); // true, prototype has firstName
function hasPrototypeProperty(object, property) {
return !object.hasOwnProperty(property) && (property in object);
}
console.log(hasPrototypeProperty(person1, "firstName")); // true
获取对象上所有可枚举的属性:Object.keys( );
获取对象上所有的属性:Object.getOwnPropertyNames()。
function Person() {
}
Person.prototype.firstName = "Wang";
Person.prototype.lastName = "Bill";
Person.prototype.sayHello = function() {
console.log("My name is " + this.firstName + " " + this.lastName);
}
var keys = Object.keys(Person.prototype); // show enumerable property
console.log("prototype:");
console.log(keys);
var person = new Person();
var pkeys0 = Object.keys(person);
console.log("instance 0:");
console.log(pkeys0);
person.name = "Bill";
person.age = 12;
var pkeys1 = Object.keys(person);
console.log("instance 1:");
console.log(pkeys1);
var protoKeys = Object.getOwnPropertyNames(Person.prototype); //show all property
console.log("getOwnPropertyNames Person.prototype:");
console.log(protoKeys);
(3)扩展基本数据类型
String.prototype.data = function(text) {
return this.indexOf(text) == 0;
}
var msg = "Prototype";
console.log(msg.data("Proto"));