JS原型对象
以下所有实例代码都是基于这个
function Person() { } Person.prototype.name = "lyx"; Person.prototype.age = 23; Person.prototype.job = "student"; Person.prototype.sayName = function () { console.log(this.name); }; var person = new Person();
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则创建一个prototype属性。这个属性指向函数的原型对象。
构造函数也是函数,只不过构造函数可以创建新的对象。
function add(a, b) { return a + b; } console.log(typeof add.prototype);//object console.log(add.prototype.constructor);//[Function: add] console.log(typeof Person.prototype); //object
在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。
console.log(Person.prototype.constructor === Person);//true
创建自定义的构造函数之后,其原型对象默认只会取得constructor属性;至于其他方法,都是从Object继承而来的。
当使用new调用一个构造函数创建一个实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMA-262管这个
指针叫[[Prototype]],firfox,safari,chrome在每个对象上都支持一个属性__proto__;
console.log(person.__proto__.age); //23 console.log(person.__proto__.name); //23
ECMA5中增加了一个新方法,叫Object.getPrototypeOf(),在所有支持的视线中,这个方法返回[[Prototype]]的值。
console.log(Object.getPrototypeOf(person) == Person.prototype);//true console.log(Object.getPrototypeOf(person).name);//lyx
使用hasOwnProperty()方法可以检测一个属性是否存在于实例中,还是存在于原型对象中。
var p = new Person(); p.email = "@wqe"; console.log(p.hasOwnProperty("name"));//false console.log(p.hasOwnProperty("email"));//true
原型与in操作符:无论属性是存在于实例中,还是原型对象中,只要能通过对象访问该属性,就是返回true
var p = new Person(); p.email = "@wqe"; console.log("name" in p); //true console.log("age" in p);//true console.log("nono" in p);//false
获取对象上所有课枚举的实例属性,可以使用ECMAscript5的Object.keys()方法。接受一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。
var p = new Person(); p.email = "@wqe"; var keys = Object.keys(p); console.log(keys);//[ 'email' ]只有email可以枚举
如果要想得到所有的实例属性,无论是否可以枚举,都可以使用Object.getOwnPropertyNames()方法得到所有的枚举属性
var p = new Person(); p.email = "@wqe"; var keys = Object.getOwnPropertyNames(p); console.log(keys);//[ 'email' ]只有email可以枚举
function Student() { } Student.prototype = { name: "lyx", age: 23, sayName: function () { return this.name; } }; var s = new Student(); console.log(s instanceof Student);//true console.log(Person.prototype.constructor === Student); //false
出现这种情况是因为我们在这里本质上完全重写了默认的prototype对象,因此constructor属性也就成了新对象的constructor属性(指向Object构造函数),不在指向Person构造函数。尽管instanceof能返回正确的结果,但constructor属性已经无法确定对象的类型了。
function Student() { } var student = new Student(); //student.sayHi(); Student.prototype.sayHi = function () { console.log("say hi"); }; student.sayHi();
看这个例子,创建了一个实例student,然后再Student的原型对象中添加了一个属性,然后再用原先创建好的实例调用这个函数属性,仍然可以访问这个方法。其原因可以归结为实例与原型之间的松散连接关系。当调用student.sayHi() 时,他首先在实例中搜索名为sayHi的属性,在没有找到的情况下,会继续搜索原型。因为实例与原型之间的连接只不过
是一个指针,而非一个副本,因此可以再原型中找到新的sayHi属性并返回保存在那里的函数。
尽管可以随时为原型添加属性和方法,并且能够立即在所有对象实例中反映出来,但如果是重写整个原型对象,那么情况就不一样了。我们知道,调用构造函数时会为实例添加一个指向最初原型的[[Prototype]]指针,而把原型修改为另外一个对象就等于切断了构造函数和最初原型之间的联系。
function Student() { } var student = new Student(); //student.sayHi(); Student.prototype = { constructor: Person, name: "", age: "", sayName: function () { console.log("hi"); } }; student.sayName();
执行报错:
TypeError: Object #<Student> has no method 'sayName' at Object.<anonymous> (e:\JetBrains\Projects\FirstDemo\Prototype_js.js:111:9) at Module._compile (module.js:456:26) at Object.Module._extensions..js (module.js:474:10) at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12) at Function.Module.runMain (module.js:497:10) at startup (node.js:119:16) at node.js:906:3
重新创建一个实例,就可以调用sayName属性
function Student() { } var student = new Student(); //student.sayHi(); Student.prototype = { constructor: Person, name: "", age: "", sayName: function () { console.log("hi"); } }; //student.sayName(); var s = new Student(); s.sayName();
给原生对象String添加一个startsWith方法
String.prototype.startsWith = function (text) { return this.indexOf(text) == 0; }; var s = "sdsdsd"; console.log(s.startsWith(s));//true
====END====