面向?qū)ο蟮男【啪?/h1>

面向?qū)ο?/h1>

本人能力有限阀趴,有誤請斧正

本文旨在復(fù)習(xí)面向?qū)ο?不包含es6)

本文學(xué)習(xí)思維

  1. 創(chuàng)建對象的方式兔朦,獲取對象屬性
  2. 構(gòu)造函數(shù),構(gòu)造函數(shù)的new 做了什么
  3. 原型與原型對象
  4. 原型鏈
  5. 繼承(借用構(gòu)造繼承徘溢、原型繼承浴滴、組合繼承茂浮、寄生組合繼承)

獲取對象屬性的三個方法

  1. for...in..

  2. Object.keys() ie9以上放心使用
    keys的支持

  3. Object.getOwnPeropertyNames 會把所有屬性枚舉出來(如數(shù)組的length)

創(chuàng)建對象的方法

  1. 字面量 var o = {};
  2. 構(gòu)造函數(shù) var o = new Object()
  3. Object.create() var o = Object.create( )

object.create()有兩個參數(shù)第一個是要新創(chuàng)建對象的原型對象, 第二個可選:自己定義的屬性,需要配置麻煩一般不用,返回一個新對象睦优,帶著指定的原型對象和屬性

通過給Object.create()參數(shù)傳null可以獲得一個純凈的沒有原型的對象菌羽。原因是null是原型鏈的鏈末

輸出查看原型鏈發(fā)現(xiàn)create在創(chuàng)造一個對象會存在傳入的屬性與方法
這個方法太適合繼承了,他會直接繼承傳入的屬性和方法
通過測試傳入{}時,也會存在Object.__proto__ 指向 Object.__proto__;

// Object.create() 實現(xiàn)方式 
// 實際這個是原型式繼承的核心
var object = function(proto){
    var F = function(){}; // 創(chuàng)建一個對象
    F.prototype = proto; //變量原型指向傳入對象
    return new F();
}

構(gòu)造函數(shù)掠械、實例、原型、原型鏈

參考資料:MDN-繼承與原型鏈

構(gòu)造函數(shù):

  1. 是一個函數(shù)
  2. 首字母大寫
  3. 使用new關(guān)鍵字創(chuàng)建

構(gòu)造函數(shù)(函數(shù)聲明或者函數(shù)表達式)本質(zhì)還是函數(shù),只是用來創(chuàng)建對象,還有個慣例就是首字母大寫

// 構(gòu)造函數(shù)
function Person(){}
// 調(diào)用構(gòu)造函數(shù)猾蒂,創(chuàng)建對象
var p1 = new Person();

為什么說構(gòu)造函數(shù)特殊呢,首先聊聊new關(guān)鍵字

參考:

  1. MDN-new運算符
  2. 高程3--p145
  3. 阮一峰博客

通過new關(guān)鍵字可以創(chuàng)建新對象均唉!

在new一個對象的時候做了什么事?4個步驟(高程3)

  1. 創(chuàng)建一個新對象
  2. 將構(gòu)造函數(shù)的作用域賦給新對象 (因此this就指向了這個新對象)
  3. 執(zhí)行構(gòu)造函數(shù)中的代碼(為這個新對象添加屬性)
  4. 返回新對象(如果構(gòu)造函數(shù)中返回了其他對象,則返回其他對象)

MDN簡化版肚菠,只討論過程舔箭,無法傳參

    // MND簡化
    
    /*
    一個繼承自 Foo.prototype 的新對象被創(chuàng)建。
    
    使用指定的參數(shù)調(diào)用構(gòu)造函數(shù) Foo 蚊逢,并將 this 綁定到新創(chuàng)建的對象层扶。
    new Foo 等同于 new Foo(),也就是沒有指定參數(shù)列表烙荷,F(xiàn)oo 不帶任何參數(shù)調(diào)用的情況镜会。
    
    由構(gòu)造函數(shù)返回的對象就是 new 表達式的結(jié)果。如果構(gòu)造函數(shù)沒有顯式返回一個對象终抽,則使用步驟1創(chuàng)建的對象戳表。
    (一般情況下,構(gòu)造函數(shù)不返回值拿诸,但是用戶可以選擇主動返回對象扒袖,來覆蓋正常的對象創(chuàng)建步驟)
    */
    
    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(/* 構(gòu)造函數(shù) */ constructor, /* 構(gòu)造函數(shù)參數(shù) */ params) {
      // 將 arguments 對象轉(zhuǎn)為數(shù)組
      var args = [].slice.call(arguments);
      // 取出構(gòu)造函數(shù)
      var constructor = args.shift();
      // 創(chuàng)建一個空對象亩码,繼承構(gòu)造函數(shù)的 prototype 屬性
      var context = Object.create(constructor.prototype);
      // 執(zhí)行構(gòu)造函數(shù)
      var result = constructor.apply(context, args);
      // 如果返回結(jié)果是對象,就直接返回野瘦,否則返回 context 對象
      return (typeof result === 'object' && result != null) ? result : context;
    }

    // 實例
    var actor = _new(Person, '張三', 28);

實例是什么

構(gòu)造函數(shù)是對一個對象的抽象描述,實例則是對象的具體表現(xiàn)

原型對象(prototype)

好吧!大boss出場描沟,都說javaScript最具有特色的就是原型

參考:

  1. 高程3 --p147

原型是什么?(高程3)

我們創(chuàng)建的每個函數(shù)都有一個prototype(原型) 屬性,這個屬性是一個指針鞭光,指向一個對象

  1. 函數(shù)的屬性
  2. 原型指向一個對象

理解原型對象 高程3 -- p148有興趣可以去讀一下

無論什么時候只要創(chuàng)建了一個新函數(shù)吏廉,就會根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個prototype屬性,這個屬性將指向函數(shù)的原型對象惰许。在默認情況下席覆,所有的原型對象會自動獲得一個constructor(構(gòu)造函數(shù))屬性,這個屬性包含一個指向prototype屬性所有函數(shù)的指針汹买。通過這個構(gòu)造函數(shù)佩伤,我們還可以繼續(xù)為原型對象添加其他屬性和方法

按照書上的理解:


MDN-prototype示意

簡述:([[Prototype]] === __proto__

  1. 所有構(gòu)造函數(shù)有一個屬性指向原型對象(prototype)
  2. 所有由構(gòu)造器生成的實例對象中有個__poroto__指向原型對象
  3. 原型對象中都有一個constructor的屬性,指向構(gòu)造函數(shù)
        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-非常具有代表的簡化原型鏈

// 讓我們假設(shè)我們有一個對象 o, 其有自己的屬性 a 和 b:
// {a: 1, b: 2}
// o 的 [[Prototype]] 有屬性 b 和 c:
// {b: 3, c: 4}
// 最后, o.[[Prototype]].[[Prototype]] 是 null.
// 這就是原型鏈的末尾晦毙,即 null生巡,
// 根據(jù)定義,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

MDN原型簡圖.png

再用對象表示一個

// 屬性遮蔽
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);

現(xiàn)在要查找p2.age屬性

  1. 實例對象中有沒有估蹄?沒有
  2. 實例對象通過__prope__找到原型對象,原型對象中有么沫换?沒有
  3. 找到Object的原型中查找有么臭蚁?沒
  4. 找到null這個對象,作為作用域的鏈末讯赏,也沒有垮兑,這個值就是undefined
prototype原型鏈與屬性屏蔽.png

屬性屏蔽就是找到了就不會再找了(實例上的屬性>原型鏈上的屬性),實際還是存在

幾種能遇到的操作符

  1. in操作符
  2. isPrototypeOf()
  3. Object.getPrototypeOf()
  4. instanceof (對象)
  5. typeof
in操作符

如果指定的屬性在指定的對象或其原型鏈中,則in 運算符返回true漱挎。語法:prop in object

isPrototypeOf()

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運算符返回一個布爾值,表示對象是否為某個構(gòu)造函數(shù)的實例磕谅。

instanceof的原理是檢查右邊構(gòu)造函數(shù)的prototype屬性私爷,是否在左邊對象的原型鏈上。(判斷他們的地址指向是否一致)膊夹。有一種特殊情況衬浑,就是左邊對象的原型鏈上,只有null對象放刨。這時工秩,instanceof判斷會失真。

幾點instanceof的注意

  1. 用于對象(由于instanceof的原理)
  2. 與null有關(guān)要注意
//instanceof判斷會失真
var obj = Object.create(null);
typeof obj // "object"
Object.create(null) instanceof Object // false


//null作為一個特殊的Object卻不屬于Object創(chuàng)建的實例进统,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
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

繼承:

繼承有幾種

高程3繼承@coderlfy.png

我用我總結(jié)了一些思維導(dǎo)圖

  1. 創(chuàng)建對象
  2. 繼承的優(yōu)缺點

這里把繼承的幾種方式羅列出來方便查閱,以下大多是代碼助币,簡易的我總結(jié)在思維導(dǎo)圖中了

1.原型(鏈)繼承

關(guān)鍵點是要打通原型鏈
由于原型對象是函數(shù)初次創(chuàng)建就會存在的對象,所以會共享
共享就會存在共享問題

優(yōu)點:

  1. 共享屬性與方法
  2. 可以通過instanceof來判斷關(guān)系

缺點:

  1. 共享問題
  2. 不能傳遞參數(shù)
        // 父類
        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

借用構(gòu)造函數(shù)

關(guān)鍵在于環(huán)境變量(this)的指向麻昼,由于每次創(chuàng)建都會創(chuàng)建一個新的this所以會擁有自己的屬性與方法,由于是改變this指向所以無法共享原型對象

優(yōu)點:

  1. 私有屬性與方法
  2. 可以傳參數(shù)

缺點:

  1. 引用類型,重復(fù)創(chuàng)建,冗余浪費內(nèi)存
  2. 無法共享
  3. 無法判斷關(guān)系
        // 父類
        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

組合式繼承

組合了原型繼承與借用構(gòu)造函數(shù)繼承繼承了優(yōu)點奠支,但是由于組合,所以創(chuàng)建了兩次對象抚芦,造成輕微的浪費空間

優(yōu)點:

  1. 私有屬性和方法
  2. 共享屬性與方法
  3. 可以確認實例與構(gòu)造函數(shù)之間的關(guān)系

缺點

  1. 造成內(nèi)存的冗余浪費
        // 父類
        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
寄生組合繼承

寄生組合繼承是把原型繼承給改掉倍谜,實際上就是想要父級的原型鏈迈螟,不一定要創(chuàng)建對象所以有了寄生組合繼承,該繼承是目前最完善的繼承方式

        // 寄生繼承
        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)
        }


參考資料:

  1. MDN-對象
  2. 冴羽的博客
  3. 阮一峰的網(wǎng)絡(luò)日志
  4. javaScript高級程序設(shè)計第三版

阮一峰的網(wǎng)絡(luò)日志:

  1. Javascript繼承機制的設(shè)計思想
  2. Javascript 面向?qū)ο缶幊蹋ㄒ唬悍庋b
  3. Javascript面向?qū)ο缶幊蹋ǘ簶?gòu)造函數(shù)的繼承
  4. Javascript面向?qū)ο缶幊蹋ㄈ悍菢?gòu)造函數(shù)的繼承
  5. 《JavaScript 標準參考教程(alpha)》,by 阮一峰

看了高程3與阮一峰老師的博客,結(jié)合起來更加好理解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者

  • 序言:七十年代末尔崔,一起剝皮案震驚了整個濱河市答毫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌季春,老刑警劉巖洗搂,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異载弄,居然都是意外死亡耘拇,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門宇攻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惫叛,“玉大人,你說我怎么就攤上這事逞刷〖斡浚” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵夸浅,是天一觀的道長仑最。 經(jīng)常有香客問我,道長帆喇,這世上最難降的妖魔是什么警医? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮番枚,結(jié)果婚禮上法严,老公的妹妹穿的比我還像新娘损敷。我一直安慰自己葫笼,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布拗馒。 她就那樣靜靜地躺著路星,像睡著了一般。 火紅的嫁衣襯著肌膚如雪诱桂。 梳的紋絲不亂的頭發(fā)上洋丐,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音挥等,去河邊找鬼友绝。 笑死,一個胖子當(dāng)著我的面吹牛肝劲,可吹牛的內(nèi)容都是我干的迁客。 我是一名探鬼主播郭宝,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼掷漱!你這毒婦竟也來了粘室?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤卜范,失蹤者是張志新(化名)和其女友劉穎衔统,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體海雪,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡锦爵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了奥裸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棉浸。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖刺彩,靈堂內(nèi)的尸體忽然破棺而出迷郑,到底是詐尸還是另有隱情,我是刑警寧澤创倔,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布嗡害,位于F島的核電站,受9級特大地震影響畦攘,放射性物質(zhì)發(fā)生泄漏霸妹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一知押、第九天 我趴在偏房一處隱蔽的房頂上張望叹螟。 院中可真熱鬧,春花似錦台盯、人聲如沸罢绽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽良价。三九已至,卻和暖如春蒿叠,著一層夾襖步出監(jiān)牢的瞬間明垢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工市咽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留痊银,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓施绎,卻偏偏與公主長得像溯革,于是被迫代替她去往敵國和親泌射。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容