javascript原型链
一、ECMAScript中描述了原型链的概念。我们知道ECMAScript并不像C++,Java那样使用类,但是对象仍然可以通过多种方式创建,其中就有构造函数方式。每个构造函数都有一个原型对象,同时都有一个prototype属性, prototype属性指向构造函数的原型对象,它被用来实现基于原型的继承和共享。而原型对象又都默认会取得一个constructor属性,这个属性包含一个指向构造函数(prototype属性所在函数)的指针。每个通过调用构造函数创建的实例对象都拥有一个指向原型对象的指针。
所有引用类型默认都继承了Object,而这个继承也是通过原型链实现的。函数是可调用的对象,所有函数的默认原型对象都是Object的实例
function Person(){} Person.prototype = { name: "bella", age: 21, sayHello: function(){ alert(this.name); }d. }
对象实例引用属性首先被查询是否包含该属性名,如果包含,该属性值就是我们想获取的,查询停止,如果不包含,会接着查询该对象的原型是否包含该属性,依此类推。我们可以随时动态地为原型添加属性和方法。而且,基于这种搜索过程,我们对原型对象所做的任何修改都能立即从对象实例上看到,即使该修改是在创建实例之后。但如果是用上面提到的语法重写整个原型对象就另当别论了。因为重写原型对象会切断现有原型对象与原来已经存在的任何对象实例之间的联系。Person.prototype.constructor = Person恢复指针
function Person(){} Person.prototype = { name: "bella", age: 21, classmates: ["Lucy", "Lily"], sayHello: function(){ alert(this.name); } } var person1 = new Person(); var person2 = new Person(); person1.classmates.push("Mark"); alert(person1.classmates === person2.classmates); //true
二、 prototype与__proto__的区别
两者都是对象类型的属性,并非所有的对象类型都有prototype属性,一般只有function对象才有prototype属性。__proto__才是真正的原型链的实际指针,然而许多浏览器并不对外公开这个属性
function
A() {};
var a1 = {};
new 操作可以分为三步:
var a1 = {};
[[Prototype]]
属性指向函数A
的原型对象:a1.[[Prototype]] = A.prototype
this
指向1中创建的对象a1
,对对象进行初始化:A.apply(a1,arguments)
function
B() {};
第一种是通过改变原型链引用地址
B.prototype.__proto__ = A.prototype
ECMA中并没有__proto__这个方法。new操作的时候,实际上只是把实例对象的原型链指向了构造函数的prototype地址块
B.prototype =
new
A();
A.
还要人为设回B本身1、面向对象应用OOP
var decimalDigits = 2, tax = 5; function add(x, y) { return x + y; } function subtract(x, y) { return x - y; } alert(add(1, 3)); //通过new 一个对象就可以调用里面的公开的方法、属性。 var Calculator = function (decimalDigits, tax) { this.decimalDigits = decimalDigits; this.tax = tax; }; Calculator.prototype = { add: function (x, y) { return x + y; }, subtract: function (x, y) { return x - y; } }; alert((new Calculator()).add(1, 3)); //利用函数自执行在加载文件同时,执行上面的JS代码,那么我们就可以访问对外公开的方法和属性,如果不通过自执行,则会报异常 var Calculaotr = function(x, y) { this.x = x; this.y = y; }; Calculaotr.prototype = function() { function add(x,y) { return x + y; }; function subtract(x,y) { return x - y; }; return { A:add, S:subtract } }();
2、继承
var BaseCalculator = function() { this.decimalDigits = 2; }; BaseCalculator.prototype = { A: function(x, y) { return x + y; }, S: function(x, y) { return x - y; } }; var Calculator = function() { this.tax = 3; }; Calculator.prototype = new BaseCalculator(); //类A的一个属性是类B型,由于它的原型是BaseCalculator的一个实例,所以不管你创建多少个Calculator对象实例,他们的原型指向的都是同一个实例。
//不想让Calculator对象访问BaseCalculator的decimalDigits属性 var BaseCalculator = function() { this.decimalDigits = 2; }; BaseCalculator.prototype = { A: function(x, y) { return x + y; }, S: function(x, y) { return x - y; } }; var Calculator = function() { this.tax = 3; }; Calculator.prototype = BaseCalculator.prototype; //重写原型 Calculaotor.prototype.add = function(x, y) { return x + y + this.tax; }
3、属性查找
当查找一个对象的属性时,会遍历原型链,一直往顶层Object找,如果没有找到,则返回undefined.
function foo() { this.add = function (x, y) { return x + y; } } foo.prototype.add = function (x, y) { return x + y + 10; } Object.prototype.subtract = function (x, y) { return x - y; } var f = new foo(); alert(f.add(1, 2)); //结果是3,而不是13 alert(f.subtract(1, 2)); //结果是-1 //add函数返回的是3,而不是13则说明,属性查找时,优先查找自己的属性。然后在往上一级找,最后找Object,这样看来,在遍历时用for in效率就是个问题。
hasOwnProperty是判断一个对象是否包含自定义属性而不是原型链上的属性,是JS中唯一一个查找属性,但不查找原型链的函数。但是JS不会保护hasOwnProperty函数,如果刚好某个对象中也有hasOwnProperty函数,则我们可以通过以下方式正确获得想要的结果:
alert({}.hasOwnProperty.call(c, 'tax'));//返回true
//这里的c是Calculator的一个对象,tax是我们要找的属性。 Object.prototype.bar = 1; var foo={moo : 1} for (var i in foo) { if(foo.hasOwnProperty(i)) { alert(console.log(i)); } }//此时只会输出moo属性