对于js中原型的理解,我仅处在初级阶段。我的理解是,js的原型是为了实现“类”的概念,可以使得对象的方法可以通用,实现类的继承。虽然js中并没有类的概念,但是多数情况下,人们还是偏向于使用面向对象的概念在编程。
Js所有的函数都有一个prototype属性,这个属性引用了一个对象,即原型对象,也简称原型。当我们用js来模拟类时就涉及到了原型链的概念。原型链和作用域链的作用很相似,都是用来定义查询变量的一种规则。当你在自身查找不到某个变量或者方法的时候,原型链会根据规则继续向上查找,直到顶端为止。
比如,在定义一个“类”的时候,通常会先定义一个构造函数,里面包含了实例属性。
function Person (name, age) { this.name = name; this.age = age; }
他可能包含了一些通用的方法(类方法),比如读取姓名和年龄,安排班级等
Person.prototype.getName = function () { return this.name; } Person.prototype.getAge = function () { return this.age; }
当我们需要给一个类定义通用的方法的时候,我们需要在它的原型上定义属性,这样,所有通过构造函数生成的实例都可以调用该方法。
继承的实现也是因为有原型的存在得以实现,通用的实现继承方法如下:
function Person (name, age) { this.name = name; this.age = age; } Person.prototype.getName = function () { return this.name; } Person.prototype.getAge = function () { return this.age; } Person.prototype.setAge = function (age) { this.age = age; } function Teacher(name,age,no){ //实现子类属性的继承,每个实例都有属于自己的属性,同时需要注意参数的顺序,call函数只看重顺序,不在乎参数名称 Person.call(this, name, age); this.classNo = no; } Teacher.prototype = new Person(); //将父类的对象赋值给子类,原型对象,这样子类就继承了父类的方法
// 在支持ES5的浏览器推荐下面的写法:(创建一个新的对象,内容是Person.prototype,将其赋值给Teacher.prototype,这样就将Person的原型和Teacher的原型相关联,并不会产生副作用)// Teacher.prototype = Object.new(Person.prototype);
Teacher.prototype.getInfo = function () { console.log('name:' + this.name + 'age:' + this.age + 'classNo:' + this.classNo); } var teacherLi = new Teacher('Li', 30, 2); teacherLi.getInfo(); //name:Liage:30classNo:2 teacherLi.setAge(28); teacherLi.getInfo(); //name:Liage:28classNo:2
一. new操作符的含义
当我们使用new操作符时,实际上就是创建一个对象。但在实际运行当中,new是创建了两个对象,并将其相互关联。例如上面的例子中var teacherLi = new Teacher('Li', 30, 2); 这句话,new操作符创建了一个对象并将它赋值给teacherLi,并将teacherLi和Teacher.prototype相联系,teacherLi.__proto__ === Teacher.prototype。
在ES5规范里,针对new的操作的定义如下:
简单理解就是:
1. 新建一个对象
2. 将对象的内部__proto__属性和构造函数的prototype相关联
3. 利用构造函数给实例对象属性赋值
4. 如果构造函数没有显示返回对象,则返回步骤一创建的对象
二.constructor
Foo.prototype.constructor === Foo
Foo.prototype的.constructor属性只是Foo函数在声明时的默认属性。如果prototype被重新赋值声明,那么constructor就不知道是指向谁了,它会根据原型链一直检索,直到检索到最上层Object对象。
function Foo() { /* .. */ } Foo.prototype = { /* .. */ }; // 创建一个新原型对象 var a1 = new Foo(); a1.constructor === Foo; // false! a1.constructor === Object; // true!
三.prototype&__proto__
对象的__proto__属性指向它关联的prototype对象。可以简单的理解为实例的__proto__属性指向它的原型对象。
function Foo(a) { this.a = a; } var foo = new Foo('foo'); console.log(foo.__proto__ === Foo.prototype); console.log(Foo.__proto__ === Function.prototype); console.log(Function.__proto__ === Function.prototype); console.log(Function.prototype.__proto__ === Object.prototype); console.log(Object.prototype.__proto__ === null);
以上的答案都是true,可以看到当你用new标识符来创建一个属性时它会默认应用建立原型链,一直关联到Object这个对象原型。
在js的内置类型当中:
Number.__proto__ === Function.prototype // true Boolean.__proto__ === Function.prototype // true String.__proto__ === Function.prototype // true Object.__proto__ === Function.prototype // true Function.__proto__ === Function.prototype // true Array.__proto__ === Function.prototype // true RegExp.__proto__ === Function.prototype // true Error.__proto__ === Function.prototype // true Date.__proto__ === Function.prototype // true Math.__proto__ === Object.prototype // true JSON.__proto__ === Object.prototype // true
因为Function.prototype.__proto__ === Object.prototype得值是true,所以,函数也是对象。
之前看到过一套测试题,放在这里以供思考:
function Foo() { getName = function () { alert (1); }; return this; } Foo.getName = function () { alert (2);}; Foo.prototype.getName = function () { alert (3);}; var getName = function () { alert (4);}; function getName() { alert (5);} //请写出以下输出结果: Foo.getName(); getName(); Foo().getName(); getName(); new Foo.getName(); new Foo().getName(); new new Foo().getName();