fred-ye / summary

my blog
43 stars 9 forks source link

[Javascript] prototype和constructor的一点笔记 #52

Open fred-ye opened 8 years ago

fred-ye commented 8 years ago

prototype和constructor

去年有一段时间被拉去做NodeJS, 于是顺带着接着写写javascript。现在开始full time做安卓,就把之前学习的一些记录一下,暂时就先告别服务器端开发,以后有好的机会再接着做做。回到主题上来:

prototype

prototype是javascript中的函数(function)的一个保留属性,并且它的值是一个对象(我们可以称这个对象为"prototype对象")

function Person() {}
var a = new Person();
a.__proto__; //输出 Person(){}
Person.prototype;//输出 Person(){}

需要注意:对象的原型是一个抽象的概念,不直接对外暴露,但是Chrome提供了proto来访问,在ES5中有Object.getPrototypeOf(obj)来获得原型。 如:

function Person() {}
var student = new Person();
student.__proto__;  //会输出Person() {}; 注意这是在chrome中

function Foo() {}
foo.prototype.__proto__ === Object.prototype //true
var obj = Object.create(null);
obj.__proto___ ; // undefined
obj.toString; //undefined
function abc () {}
abc.prototype; // abc {}
var binded = abc.bind(null);
typeof binded // "function"
binded.prototype //undefined

bind函数是修改函数运行时的this 一个例子:

function Foo () {}
Foo.prototype.z = 3;
var one = new Foo();
one.x = 1;
one.y = 2;
console.log(one.x);// 1
console.log(one.y);// 2
console.log(one.z);// 3
console.log(one.prototype); //undefined
console.log(typeof Foo.prototype);//object
console.log(Foo.prototype.constructor);//function Foo() {}

修改prototype

function Student() {}
var bosn = new Student();
Student.prototype.x = 101;
bosn.x; // 101
Student.prototype = {y: 2}; 
bosn.y; //undefined   原有对象不能访问原型的新增属性
bosn.x; //101

动态修改函数的prototype时,第3行代码对Student的原型动态添加一个属性时,所有的Student对象都会受到影响。如果直接修改构造器的prototype对象---Student.prototype = {y: 2}时,对已经创建的实例是不会有影响的。已经创建的实例,其已经指向了Student.prototype所指向的那个对象。

constuctor

接着看constructor属性,前面提到了每个函数都有一个prototype属性,其prototype属性中会有一个constructor属性,该属性会指向函数本身。所以上面例子的最后一行,输出的会是函数Foo本身。 针对上面的例子。如果有 console.log(one.constructor)也会有输出function Foo(){},因为one的原型会指向Foo.prototype,而Foo.prototype所对应的对象上有constructor属性。

看下面这个例子:

function Person(name) {
    this.name = name;
    this.showMe = function() {
        console.log(this.name);
    }
}
Person.prototype.from = function() {
    console.log('I come from prototype.');
}
function SubPerson() {}
SubPerson.prototype = new Person();
var subOne = new SubPerson();
subOne.from(); // I come from prototype
console.log(subOne.constructor);
console.log(SubPerson.prototype.constructor);

说明:

于是,便找了到Person.prototype.constructor, 因此,此处会输出function Person(name) {...};

SubPerson.prototype.constructor: SubPerson.prototype指向的是一个new Person()对象,但该new Person()对象没有constructor属性,于是从这个new Person()类型的对象的构造器(Person函数)的prototype中找,也就找到了。因此这一行的输出和上一行的一样。

继承

继承的实现很简单,只需要把子类的prototype设置为父类的一个(实例化)对象即可。 看下面的例子:

function Person(name) {
    this.name = name;
    this.showMe = function() {
        console.log(this.name);
    }
}
Person.prototype.from = function() {
    console.log('I come from prototype.');
}
var father = new Person('js');
console.log(father.constructor);// function Person(name) {...}

function SubPerson() {}

SubPerson.prototype = father;
SubPerson.prototype.constructor = SubPerson; //constructor是指向函数

var son = new SubPerson();
son.showMe(); // js
son.from(); // I come from prototype
console.log(father.constructor); //function SubPerson() {}
console.log(son.constructor); //function SubPerson() {}
console.log(SubPerson.prototype.constructor);//function SubPerson() {}

需要注意的是倒数第三行console.log(father.constructor); 输出的会是function SubPerson() {}, 这是因为在

SubPerson.prototype = father;
subPerson.prototype.constructor = SubPerson; //constructor是指向函数

subPerson.prototype.constructor = SubPerson; 也就相当于father.constructor = SubPerson

对象的创建方式

除了采用new来创建对象,也可以采用Object.create()来创建对象。通过new去创建对象, 它的原型会指向构造器(函数)的prototype属性。

Object.create(proto [, propertiesObject ])是E5中提出的一种新的对象创建方式,第一个参数是要继承的原型

var obj  = Objcet.create(obj); obj的原型指向create()中的参数
function Person(name, age) {
    this.name = name;
    this.age = age;
} 
Person.prototype.hi = function() {
    console.log('name:' + name + '   age:' + age);
}
Person.prototype.LEGS_NUM = 2; //Person类的所有实例共享
Person.prototype.ARMS_NUM = 2;
Person.prototype.walk = function() {
    console.log(this.name + ' is walking....');
}
function Student(name, age, className) {
    Person.call(this, name, age);
    this.className = className;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

覆盖基类的方法

Student.prototype.hi = function() {
    console.log('name:' + this.name + '  age:' + this.age + '   ' + ' className:' + className);
}

子类自己的方法

Student.prototype.learn = function(subject) {
    console.log(this.name + ' is learning ' + subject);
}

推荐采用这种方式来创建对象。公有的属性写在函数中,公有的方法写在函数的prototype属性上。采用Object.create来创建对象。

参考: http://blog.csdn.net/niuyongjie/article/details/4810835 http://www.imooc.com/learn/277