本人能力有限,有誤請斧正javascript
本文旨在複習面向對象(不包含es6)html
本文學習思惟java
keys的支持git
var o = {};
var o = new Object()
var o = Object.create( )
object.create()有兩個參數第一個是要新建立對象的原型對象, 第二個可選:本身定義的屬性,須要配置麻煩通常不用,返回一個新對象,帶着指定的原型對象和屬性es6
經過給Object.create()參數傳null能夠得到一個純淨的沒有原型的對象。緣由是null是原型鏈的鏈末github
輸出查看原型鏈發現create在創造一個對象會存在傳入的屬性與方法
這個方法太適合繼承了,他會直接繼承傳入的屬性和方法
經過測試傳入{}時,也會存在Object.__proto__ 指向 Object.__proto__
;編程
// Object.create() 實現方式 // 實際這個是原型式繼承的核心 var object = function(proto){ var F = function(){}; // 建立一個對象 F.prototype = proto; //變量原型指向傳入對象 return new F(); }
參考資料:MDN-繼承與原型鏈數組
構造函數(函數聲明或者函數表達式)本質仍是函數,只是用來建立對象,還有個慣例就是首字母大寫網絡
// 構造函數 function Person(){} // 調用構造函數,建立對象 var p1 = new Person();
參考:app
經過new關鍵字能夠建立新對象!
在new一個對象的時候作了什麼事?4個步驟(高程3)
MDN簡化版,只討論過程,沒法傳參
// MND簡化 /* 一個繼承自 Foo.prototype 的新對象被建立。 使用指定的參數調用構造函數 Foo ,並將 this 綁定到新建立的對象。 new Foo 等同於 new Foo(),也就是沒有指定參數列表,Foo 不帶任何參數調用的狀況。 由構造函數返回的對象就是 new 表達式的結果。若是構造函數沒有顯式返回一個對象,則使用步驟1建立的對象。 (通常狀況下,構造函數不返回值,可是用戶能夠選擇主動返回對象,來覆蓋正常的對象建立步驟) */ var Foo = function(){}; var _new2 = function(fn){ var o = Object.create(fn.prototype); var k = fn.call(o); if(typeof k === 'object'){ return k; }else{ return o; } } var f = _new2(Foo);
阮老師的版本能夠傳參,並且很詳細了
function _new(/* 構造函數 */ constructor, /* 構造函數參數 */ params) { // 將 arguments 對象轉爲數組 var args = [].slice.call(arguments); // 取出構造函數 var constructor = args.shift(); // 建立一個空對象,繼承構造函數的 prototype 屬性 var context = Object.create(constructor.prototype); // 執行構造函數 var result = constructor.apply(context, args); // 若是返回結果是對象,就直接返回,不然返回 context 對象 return (typeof result === 'object' && result != null) ? result : context; } // 實例 var actor = _new(Person, '張三', 28);
構造函數是對一個對象的抽象描述,實例則是對象的具體表現
好吧!大boss出場,都說javaScript最具備特點的就是原型
參考:
原型是什麼?(高程3)
咱們建立的每一個函數都有一個prototype(原型) 屬性,這個屬性是一個指針,指向一個對象
理解原型對象 高程3 -- p148有興趣能夠去讀一下
不管何時只要建立了一個新函數,就會根據一組特定的規則爲該函數建立一個prototype屬性,這個屬性將指向函數的原型對象。在默認狀況下,全部的原型對象會自動得到一個constructor(構造函數)屬性,這個屬性包含一個指向prototype屬性全部函數的指針。經過這個構造函數,咱們還能夠繼續爲原型對象添加其餘屬性和方法
按照書上的理解:
簡述:([[Prototype]] === __proto__
)
__poroto__
指向原型對象constructor
的屬性,指向構造函數var Person = function() {}; Person.prototype.age = 1; var p = new Person(); var p2 = new Person(); console.log(p.__proto__ === Person.prototype); // true console.log(p2.__proto__ === Person.prototype); // true console.log(Person === Person.prototype.constructor); // true
原型鏈就是在查找到某個屬性或者方法不斷向上查找的一個過程
MDN-很是具備表明的簡化原型鏈
// 讓咱們假設咱們有一個對象 o, 其有本身的屬性 a 和 b: // {a: 1, b: 2} // o 的 [[Prototype]] 有屬性 b 和 c: // {b: 3, c: 4} // 最後, o.[[Prototype]].[[Prototype]] 是 null. // 這就是原型鏈的末尾,即 null, // 根據定義,null 沒有[[Prototype]]. // 綜上,整個原型鏈以下: // {a:1, b:2} ---> {b:3, c:4} ---> null console.log(o.a); // 1 // a是o的自身屬性嗎?是的,該屬性的值爲1 console.log(o.b); // 2 // b是o的自身屬性嗎?是的,該屬性的值爲2 // 原型上也有一個'b'屬性,可是它不會被訪問到.這種狀況稱爲"屬性遮蔽 (property shadowing)" console.log(o.c); // 4 // c是o的自身屬性嗎?不是,那看看原型上有沒有 // c是o.[[Prototype]]的屬性嗎?是的,該屬性的值爲4 console.log(o.d); // undefined // d是o的自身屬性嗎?不是,那看看原型上有沒有 // d是o.[[Prototype]]的屬性嗎?不是,那看看它的原型上有沒有 // o.[[Prototype]].[[Prototype]] 爲 null,中止搜索 // 沒有d屬性,返回undefined
再用對象表示一個
// 屬性遮蔽 function Person() { this.name = '111'; } Person.prototype.name = '222'; var p1 = new Person(); console.log(p1.name); // 111 console.log(p1.__proto__.name); // 222 var p2 = new Person(); console.log(p2.age);
如今要查找p2.age屬性
__prope__
找到原型對象,原型對象中有麼?沒有undefined
屬性屏蔽就是找到了就不會再找了(實例上的屬性>原型鏈上的屬性),實際仍是存在
若是指定的屬性在指定的對象或其原型鏈中,則in 運算符返回true。語法:
prop in object
isPrototypeOf() 方法用於測試一個對象是否存在於另外一個對象的原型鏈上。
function Foo() {} function Bar() {} function Baz() {} Bar.prototype = Object.create(Foo.prototype); Baz.prototype = Object.create(Bar.prototype); var baz = new Baz(); console.log(Baz.prototype.isPrototypeOf(baz)); // true console.log(Bar.prototype.isPrototypeOf(baz)); // true console.log(Foo.prototype.isPrototypeOf(baz)); // true console.log(Object.prototype.isPrototypeOf(baz)); // true
instanceof的原理是檢查右邊構造函數的prototype屬性,是否在左邊對象的原型鏈上。(判斷他們的地址指向是否一致)。有一種特殊狀況,就是左邊對象的原型鏈上,只有null對象。這時,instanceof判斷會失真。
幾點instanceof的注意
//instanceof判斷會失真 var obj = Object.create(null); typeof obj // "object" Object.create(null) instanceof Object // false //null做爲一個特殊的Object卻不屬於Object建立的實例,null原型鏈的鏈末 undefined instanceof Object // false null instanceof Object // false // instanceof 用於對象 var str = '1' var str2 = new String('2'); str instanceof String // false str2 instanceof String // true
typeof 1 //number typeof '' // string typeof undefined //undefined typeof true // boolean typeof function(){} // function typeof {} // object typeof [] // object typeof null // object typeof Symbol() //symbol ES6
繼承有幾種
我用我總結了一些思惟導圖
這裏把繼承的幾種方式羅列出來方便查閱,如下大可能是代碼,簡易的我總結在思惟導圖中了
關鍵點是要打通原型鏈
因爲原型對象是函數初次建立就會存在的對象,因此會共享
共享就會存在共享問題
優勢:
缺點:
// 父類 function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function () { return this.property; } // 子類 function SubType() { this.subproperty = false; } // 繼承父類 打通原型鏈 SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function () { return this.subproperty; } var instance = new SubType(); console.log(instance.getSuperValue()); // true
關鍵在於環境變量(this)的指向,因爲每次建立都會建立一個新的this因此會擁有本身的屬性與方法,因爲是改變this指向因此沒法共享原型對象
優勢:
缺點:
// 父類 function SuperType() { this.colors = ['red']; } // 子類 function SubType() { // 繼承父類 SuperType.call(this); } var instance1 = new SubType(); colors.push('black'); cosnole.log(instance1.colors); // red,black var instance2 = new SubType(); console.log(instance2.colors); // red
組合了原型繼承與借用構造函數繼承繼承了優勢,可是因爲組合,因此建立了兩次對象,形成輕微的浪費空間
優勢:
缺點
// 父類 function SuperType(name) { this.name = name; this.colors = ['red']; } SuperType.prototype.sayName = function () { console.log(this.name); } // 子類 function SubType(name, age) { // 繼承屬性 SuperType.call(this, name); this.age = age; } // 繼承方法 SubType.prototype = new SuperType(); SubType.prototype.sayAge = function () { console.log(this.age); } var instance1 = new SubType('name1', 1); instance1.colors.push('black'); console.log(instance1.colors); // red,black instance1.sayName(); // name1 instance1.sayAge(); // 1 var instance2 = new SubType('name2', 2); console.log(instance2.colors); // red instance2.sayName(); // name2 instance2.sayAge(); // 2
寄生組合繼承是把原型繼承給改掉,實際上就是想要父級的原型鏈,不必定要建立對象因此有了寄生組合繼承,該繼承是目前最完善的繼承方式
// 寄生繼承 function inheritPrototype(subType, superType) { var prototype = Object.create(superType.prototype); prototype.constructor = subType; subType.prototype = prototype; } // 父類 function SuperType(name) { this.name = name; this.colors = ['red']; } SuperType.prototype.sayName = function () { console.log(this.name); } // 子類繼承 function SubType(name, age) { SuperType.call(this, name); } inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function () { console.log(this.age) }
阮一峯的網絡日誌:
看了高程3與阮一峯老師的博客,結合起來更加好理解