在構造函數建立的時候,系統默認的幫構造函數建立並關聯一個對象 這個對象就是原型java
在原型中的全部屬性和方法,均可以被和其關聯的構造函數建立出來的全部的對象共享react
構造函數名.prototype 實例化的對象.__proto __c++
function Person (name) {
this.name = name
}
Person.prototype.fn = function () {
console.log('hello world')
console.log(this.name)
}
var p = new Person('小紅')
p.fn()
p.name = '曉麗'
p.fn()
console.log(Person.prototype)
console.log(p)
複製代碼
含義: 是一個函數的屬性,這個屬性是一個指針,指向一個對象 做用: 構造函數調用 訪問該構造函數所關聯的原型對象編程
含義: 是一個對象擁有的內置屬性,是js內部使用尋找原型鏈的屬性,經過該屬性能夠容許實例對象直接訪問到原型bash
含義:原型對象的constructor 指向其構造函數,若是替換了原型對象以後,這個constructor屬性就不許確,須要手動補充一下 app
擴展以及延伸
在不少編程語言中,如java,objectC,c++等,都存在類的概念,類中有私有屬性,私有方法等,經過類來實現面對對象的繼承,可是,在ES5以及之前中不像上面這幾種語言同樣,有嚴格的類的概念。js經過構造函數以及原型鏈來實現繼承。編程語言
var obj = new Date()
// 能夠分解爲
var obj = {};
obj.__proto__ = Date.prototype;
Base.call(obj)
複製代碼
構造函數與普通函數的區別函數
//構造函數
function Egperson (name,age) {
this.name = name;
this.age = age;
this.sayName = function () {
alert(this.name);
}
}
var person = new Egperson('mike','18'); //this-->person
person.sayName(); //'mike'
//普通函數
function egPerson (name,age) {
this.name = name;
this.age = age;
this.sayName = function () {
alert(this.name);
}
}
egPerson('alice','23'); //this-->window
window.sayName(); //'alice'
複製代碼
function CreatePerson(name, age, gender){
var obj = {};
obj.name = name;
obj.age = age;
obj.gender = gender;
//因爲是函數調用模式,因此this打印出來是window
console.log(this);
return obj;
}
var p = CreatePerson("小明", 18, "male"); // 調用方式是函數的調用方式
複製代碼
function CreatePerson(name, age, gender){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.gender = gender;
//這裏的this指向new 建立出來的對象
console.log(this);
return obj;
}
var p = new CreatePerson("小明", 18, "male"); // 調用方式是函數的調用方式
複製代碼
function Person(name, age, job) {
//屬性
this.name = name;
this.age = age;
this.job = job;
//方法
if(typeof this.sayName != "function") {
//全部的公有方法都在這裏定義
Person.prototype.sayName = function() {
alert(this.name);
};
Person.prototype.sayJob = function() {
alert(this.job);
};
Person.prototype.sayAge = function() {
alert(this.age);
};
}
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.sayName(); //Nicholas
person2.sayName(); //Greg
複製代碼
js實現繼承的方式: 混入式繼承,原型繼承以及經典繼承,ES6的Class也能夠實現繼承ui
基本上,ES6 的class能夠看做只是一個語法糖,它的絕大部分功能,ES5 均可以作到,新的class寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已。this
// ES5
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
// ES6
//定義類
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
複製代碼
每個對象都有__proto__屬性,指向對應的構造函數的prototype屬性。Class 做爲構造函數的語法糖,同時有prototype屬性和__proto__屬性,所以同時存在兩條繼承鏈。
class A {
}
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
複製代碼
類的繼承內部實現
class A {
}
class B {
}
// B 的實例繼承 A 的實例
Object.setPrototypeOf(B.prototype, A.prototype);
// B 繼承 A 的靜態屬性
Object.setPrototypeOf(B, A);
const b = new B();
Object.setPrototypeOf = function (obj, proto) {
obj.__proto__ = proto;
return obj;
}
複製代碼
做爲一個對象,子類(B)的原型(__proto__屬性)是父類(A);
做爲一個構造函數,子類(B)的原型對象(prototype屬性)是父類的原型對象(prototype屬性)的實例。
Object.create(A.prototype);
// 等同於
B.prototype.__proto__ = A.prototype;
複製代碼
子類實例的__proto__屬性的__proto__屬性,指向父類實例的__proto__屬性。也就是說,子類的原型的原型,是父類的原型。
var p1 = new Point(2, 3);
var p2 = new ColorPoint(2, 3, 'red');
p2.__proto__ === p1.__proto__ // false
p2.__proto__.__proto__ === p1.__proto__ // true
複製代碼
類的方法內部含有this,默認指向類的實例。可是當類中的實例方法提取出來使用的時候,this指向運行時所在環境。
解決方法(新版react中,在聲明綁定方法的時候 三種方式與此相同)
class Logger {
constructor() {
this.printName = this.printName.bind(this);
}
// ...
}
class Logger {
constructor() {
this.printName = (name = 'there') => {
this.print(`Hello ${name}`);
};
}
// ...
}
複製代碼
在ES5中,繼承實質上是子類先建立屬於本身的this,而後再將父類的方法添加到this(也就是使用Parent.apply(this)的方式
而在ES6中,則是先建立父類的實例對象this,而後再用子類的構造函數修改this。