基本都是廖学峰老师JavaScript教程的内容。先前看也似是懂了,本身稍微理下做复习。javascript
JavaScript 的原型链和 Java 的 Class 区别就在, 它没有“Class”的概念, 全部对象都是实例,所谓继承关系不过是把一个对象的原型指向另外一个对象而已。java
Student
是一个现有的对象,不一样于C++,Python中类对象的概念,Student
更符合实例对象。Student
有name
属性和run()
方法。xm
只有name
属性,Student
和xm
是同等地位的实例对象。xm.__proto__ = Student;
把xm
的原型指向了对象Student
,看上去 xm
仿佛是从Student
继承下来的。xm
能够调用Student
的run()
方法。数组
var Student = { name: 'Robert', run: function() { console.log(this.name + ' is running...'); } }; var xm = { name: 'xiaoming' } xm.__proto__ = Student; console.log(xm.name); xm.run(); console.log(Student.name); Student.run(); // run output: // xiaoming // xiaoming is running... // Robert // Robert is running...
上述代码仅用于演示目的。在编写 JavaScript 代码时,不要直接用 obj.__proto__
去改变一个对象的原型,而且,低版本的IE也没法使用__proto__
。
Object.create()
方法能够传入一个原型对象,并建立一个基于该原型的新对象, 可是新对象什么属性都没有。能够编写一个函数来建立新对象。函数
xm
对象的__proto__
属性是{ name: 'Robert', run: [Function: run] }
,而没有prototype
属性。this
var Student = { name: 'Robert', run: function() { console.log(this.name + ' is running...'); } }; function createStudent(name) { // 传入一个原型对象,建立一个新的原型对象 var s = Object.create(Student); s.name = name; return s; } var xm = createStudent('xiaoming'); xm.run() // xiaoming is running... // xm 对象的__proto__ 和 prototype console.log(xm.__proto__) //{ name: 'Robert', run: [Function: run] } console.log(xm.prototype) // undefined // 原型链 console.log(xm.__proto__ === Student); console.log(Student.__proto__ === Object.__proto__.__proto__); console.log(Object.__proto__.__proto__.__proto__ === null);
对象xm
原型链为:
xm --> Student.__proto__ --> Object.__proto__ --> null
prototype
除了直接用{ ... }建立一个对象外,JavaScript还能够用一种构造函数的方法来建立对象。它的用法是,先定义一个构造函数,再用关键字new来调用这个函数,并返回一个对象。构造函数和普通函数并没有差异。若是不写new,它就是一个普通函数,它返回undefined。可是,若是写了new,它就变成了一个构造函数,它绑定的this指向新建立的对象,并默认返回this,也就是说,不须要在最后写return this。code
function Student(name) { this.name = name; this.hello = function () { console.log('Hello, ' + this.name); }; } var jack = new Student('jack'); console.log(jack.name); //jack jack.hello(); //Hello, jack console.log(jack.__proto__ === Student.prototype) console.log(Student.prototype.__proto__ === Object.prototype) console.log(Object.prototype.__proto__ === null)
对象jack
的原型链是:
jack --> Student.prototype --> Object.prototype --> null
用new Student()建立的对象还从原型上得到了一个constructor属性,它指向函数Student自己:对象
console.log(jack.constructor === Student.prototype.constructor) // true console.log(Student.prototype.constructor === Student) // true console.log(Object.getPrototypeOf(jack) === Student.prototype) // true console.log(jack instanceof Student) // true
xm
相似字符串对象str
也只有__proto__
属性,没有prototype
属性。str
的原型链是:str --> String.prototype --> Object.prototype --> null
var str = 'aaaaaaaa' //对象的__proto__ 和 prototype console.log(str.__proto__) //[String: ''] console.log(str.prototype) //undefined console.log(typeof(str.__proto__)) //object // 原型链 console.log(str.__proto__ === String.prototype) //true console.log(String.prototype.__proto__ === Object.prototype) //true console.log(Object.prototype.__proto__ === null) //true // 构造函数 console.log(str.constructor === String.prototype.constructor) //true console.log(String.prototype.constructor === String) //true console.log(Object.prototype.constructor === Object) //true // 类型 console.log(typeof(String.prototype)) //object console.log(typeof(String.prototype.__proto__)) //object console.log(typeof(Object.prototype)) //object console.log(typeof(Object.prototype.__proto__)) //object
函数对象的原型链,函数对象不只有__proto__
,还有属性prototype
。
那么Function()
,Object()
和前面的String()
是函数对象,由于它们有prototype
属性。
经过new Foo()
构建的对象foo_object
的原型链是:
foo_object --> Foo.prototype --> Object.prototype --> null
函数Foo()
也是一个对象,它的原型链是:
Foo() --> Function.prototype --> Object.prototype --> null
blog
function Foo() { return 0; } var foo_object = new Foo(); // 函数对象的__proto__ 和 prototype console.log(Foo.__proto__) //[Function] console.log(Foo.prototype) //Foo {} console.log(typeof(Foo.__proto__)) //function console.log(typeof(Foo.prototype)) //object // 原型链 console.log(foo_object.__proto__ === Foo.prototype); // true console.log(Foo.prototype.__proto__ === Object.prototype); // true console.log(Object.prototype.__proto__ === null) // true console.log(Foo.__proto__ === Function.prototype); // true console.log(Function.prototype.__proto__ === Object.prototype); // true console.log(Object.prototype.__proto__ === null) // true // 构造函数 console.log(Foo.constructor === Function.prototype.constructor) //true console.log(Function.prototype.constructor === Function) //true console.log(Object.prototype.constructor === Object) //true // 类型 console.log(typeof(Function.prototype)) //function console.log(typeof(Function.prototype.__proto__)) //object console.log(typeof(Object.prototype)) //object console.log(typeof(Object.prototype.__proto__)) //object
1).全部对象有属性__proto__
,指向对象的原型对象。
2).函数对象除了有属性__proto__
,还有属性prototype
,prototype
属性指向(构造)函数对象共享的属性和方法(即一个由此构造函数构造而来的对象能够继承prototype指向的属性及方法),prototype
属性使您有能力向对象添加属性和方法。其中有一个constructor
属性指向函数对象自己(String.prototype.constructor === String
,Function.prototype.constructor === Function
)。继承
展现下如何使用 prototype 属性来向对象添加属性:
1)经过Student.prototype
的方式向函数对象添加了age
和hello()
属性,后续构建的对象jack
和sara
都继承了所增属性。
2)hello()
属性分别在sara
对象构建以前和jack
对象构建以后添加的,但继承获得的hello()
是同样的。那么在JS中一个已构建的对象,还能够经过其原型对象“不着痕迹”的扩展属性。
function Student(name) { this.name = name; } Student.prototype.age = 23; var jack = new Student('jack'); Student.prototype.hello = function () { console.log('Hello, ' + this.name + '!'); }; var sara = new Student('sara') console.log(jack.age); //23 jack.hello(); //Hello, jack! console.log(sara.age) //23 sara.hello() //Hello, sara! console.log(sara.hello === jack.hello) //true
JavaScript因为采用原型继承,咱们没法直接扩展一个Class,由于不存在Class这种类型。
Student
的构造函数
function Student(props) { this.name = props.name || 'Unnamed'; } Student.prototype.hello = function () { console.log('Hello, ' + this.name + '!'); }
若是要基于Student
扩展出PrimaryStudent
,能够先定义出PrimaryStudent
:
function PrimaryStudent(props) { // 调用Student构造函数,绑定this变量: Student.call(this, props); this.grade = props.grade || 1; }
可是,调用了Student
构造函数不等于继承了Student
,PrimaryStudent
建立的对象的原型链仍是:
new PrimaryStudent() --> PrimaryStudent.prototype --> Object.prototype --> null
要想办法把原型链修改成:
new PrimaryStudent() --> PrimaryStudent.prototype --> Student.prototype --> Object.prototype --> null
这样,原型链对了,继承关系就对了。新的基于PrimaryStudent
建立的对象不但能调用PrimaryStudent.prototype
定义的方法,也能够调用Student.prototype
定义的方法。
可借助一个中间对象来实现正确的原型链,中间对象能够用一个空函数F来实现,原型指向Student.prototype
。代码以下:
// Student构造函数: function Student(props) { this.name = props.name || 'Unnamed'; } Student.prototype.hello = function () { console.log('Hello, ' + this.name + '!'); } // PrimaryStudent构造函数: function PrimaryStudent(props) { Student.call(this, props); this.grade = props.grade || 1; } // 中间对象-空函数F(): function F() { } // step1:把F的原型指向Student.prototype: F.prototype = Student.prototype; // step2:把PrimaryStudent的原型指向一个新的F对象,F对象的原型正好指向Student.prototype: PrimaryStudent.prototype = new F(); //PrimaryStudent.prototype.__proto__ === F.prototype === Student.prototype // step3:把PrimaryStudent原型的构造函数修复为PrimaryStudent: PrimaryStudent.prototype.constructor = PrimaryStudent; // 继续在PrimaryStudent原型(就是new F()对象)上定义方法: PrimaryStudent.prototype.getGrade = function () { return this.grade; }; // 建立xiaoming: var xiaoming = new PrimaryStudent({ name: '小明', grade: 2 }); xiaoming.name; // '小明' xiaoming.grade; // 2 // 验证原型链: console.log(xiaoming.__proto__ === PrimaryStudent.prototype); // true console.log(PrimaryStudent.prototype.__proto__ === Student.prototype); // true console.log(Student.prototype.__proto__ === Object.prototype); // true // 验证继承关系: console.log(xiaoming instanceof PrimaryStudent); // true console.log(xiaoming instanceof Student); // true
新的关键字class从ES6开始正式被引入到JavaScript中。
若是用新的class
关键字来编写Student,并且能够直接经过extends
来实现继承,原型继承的中间对象,原型对象的构造函数等都不须要考虑了。
使用class
定义的对象如Student
仍是函数对象,class
的目的就是让定义类更简单。class
的定义包含了构造函数constructor
和定义在原型对象上的函数hello()
(注意没有function关键字),这样就避免了Student.prototype.hello = function () {...}
这样分散的代码。
// class关键 class Student { constructor(name) { this.name = name; } hello() { console.log('Hello, ' + this.name + '!'); } } console.log(typeof(Student)); //function var xiaoming = new Student('小明'); xiaoming.hello(); // Hello, 小明! class PrimaryStudent extends Student { constructor(name, grade) { super(name); // 记得用super调用父类的构造方法! this.grade = grade; } myGrade() { console.log('I am at grade ' + this.grade); } } var xiaohong = new PrimaryStudent('小红', 24) xiaohong.hello() // Hello, 小红! xiaohong.myGrade() // I am at grade 24
ES6引入的class和原有的JavaScript原型继承有什么区别呢?实际上它们没有任何区别,class的做用就是让JavaScript引擎去实现原来须要咱们本身编写的原型链代码。简而言之,用class的好处就是极大地简化了原型链代码。