JS面向?qū)ο笳砥弧A(chǔ)概念衍生

JS面向?qū)ο?/h1>

oop

  • 繼承:實例可以繼承A對象中的方法和屬性,減少代碼冗余

  • 封裝:對象把實現(xiàn)過程封裝在方法中,調(diào)用者可以不需了解過程直接調(diào)用

  • 多態(tài):一種事物,可以有多種表現(xiàn)形式

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

是對象的模版

function Person(){
    this.name = 'king';
    var age = 12;   // 私有屬性镜会,是指針針對實例而言
    this.say = function() {
        console.log('hi~')
    }
}

對象

  • 普通對象: 沒有prototype,有proto
  • 函數(shù)對象: 只有函數(shù)有prototype屬性终抽,所有的對象都有proto隱式屬性
  • 實例對象: var son = new Person();

創(chuàng)建對象方式

new運算符

MDN:語法new constructor[([arguments])]戳表,創(chuàng)建一個用戶定義的對象類型的實例或具有構(gòu)造函數(shù)的內(nèi)置對象的實例

  • 創(chuàng)建一個空的簡單JavaScript對象(即{})
  • 鏈接該對象(即設(shè)置該對象的構(gòu)造函數(shù))到另一個對象
  • 將步驟1新創(chuàng)建的對象作為this的上下文
  • 如果該函數(shù)沒有返回對象,則返回this

但實際上new具體做了什么操作

var son = new Person();

當這段代碼運行的時候昼伴,內(nèi)部實際上執(zhí)行的是:

// 創(chuàng)建一個空對象
var other = new Object();
// 將空對象的原型賦值為構(gòu)造函數(shù)的原型
other.__proto__ = Person.prototype;
// 改變this指向
Person.call(other);

最后一步如何理解匾旭,當構(gòu)造函數(shù)是否返回對象,可做如下嘗試

// 無返回對象時
function Person(name){
    this.name = name;
    this.age = 12;
}
Person.prototype.say = function(){
    console.log('say hi')
}
var son = new Person('king');
console.log(son.name);   // 'king'
console.log(son.say());  // 'say hi'

由此可以得出結(jié)論圃郊,new通過構(gòu)造函數(shù)Person創(chuàng)造出來的實例son,可以訪問Person中的內(nèi)部屬性价涝,以及原型鏈上的方法,當對構(gòu)造函數(shù)Person如下修改時

// 返回非對象
function Person(name){
    this.name = name;
    this.age = 12;
    return 1
}
Person.prototype.say = function(){
    console.log('say hi')
}
var son = new Person('king');
console.log(son.name);   // 'king'
console.log(son.say());  // 'say hi'
// 返回對象
function Person(name){
    this.name = name;
    this.age = 12;
    return {color: 'red'}
}
Person.prototype.say = function(){
    console.log('say hi')
}
var son = new Person('king');
console.log(son);   // '{color: "red"}'

綜上持舆,可以很好理解MDN上關(guān)于new操作符的最后一步操作結(jié)果

Object.create()

語法:Object.create(proto[, propertiesObject])色瘩,創(chuàng)建一個新對象,使用現(xiàn)有的對象來提供新創(chuàng)建的對象的proto

內(nèi)部實現(xiàn)方式

Object.create =  function (o) {
    // o參數(shù)是原型對象逸寓,不需要加.prototype
    var F = function () {};
    F.prototype = o;
    return new F();
};
Object.create = function (obj) {
    var B={};
    Object.setPrototypeOf(B,obj); // or  B.__proto__=obj;
    return B;  
};
MDN demo:
const person = {
  isHuman: false,
  printIntroduction: function() {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
  }
};

const me = Object.create(person);

console.log(me): // {}
me.name = 'Matthew'; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten

me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"

第二個參數(shù)

//該參數(shù)是一個屬性描述對象居兆,它所描述的對象屬性,會添加到實例對象竹伸,作為該對象自身的屬性泥栖。
var obj = Object.create({}, {
  p1: {
    value: 123,
    enumerable: true,
    configurable: true,
    writable: true,
  },
  p2: {
    value: 'abc',
    enumerable: true,
    configurable: true,
    writable: true,
  }
});

// 等同于
var obj = Object.create({});
obj.p1 = 123;
obj.p2 = 'abc';

個人感覺跟new的用法非常相似,區(qū)別在于:

  • 字面量和new關(guān)鍵字創(chuàng)建的對象是Object的實例佩伤,原型指向Object.prototype聊倔,繼承內(nèi)置對象Object

  • Object.create(proto, propertiesObject)創(chuàng)建的對象的原型取決于proto,proto為null生巡,新對象是空對象耙蔑,沒有原型,不繼承任何對象孤荣;proto為指定對象甸陌,新對象的原型指向指定對象,繼承指定對象盐股。propertiesObject是可選參數(shù)钱豁,指定要添加到新對象上的可枚舉的屬性(即其自定義的屬性和方法,可用hasOwnProperty()獲取的疯汁,而不是原型對象上的)的描述符及相應(yīng)的屬性名稱,如果不傳則實例對象為{}牲尺。

  • Object.create(o),如果o是一個構(gòu)造函數(shù),則采用這種方法來創(chuàng)建對像沒有意義

  • Object.create(o),如果o是一個字面量對象或?qū)嵗龑ο螅敲聪喈斢谑菍崿F(xiàn)了對象的淺拷貝

封裝

把"屬性"(property)和"方法"(method)谤碳,封裝到一個構(gòu)造函數(shù)里溃卡,并且他的實例對象可以繼承他的所有屬性和方法,構(gòu)建構(gòu)造函數(shù)

prototype

解決所有實例指向prototype對象地址蜒简,而不需要每個實例對象重復(fù)生成構(gòu)造內(nèi)部屬性方法瘸羡,這意味著,我們可以把那些不變的屬性和方法搓茬,直接定義在prototype對象上犹赖,被構(gòu)造函數(shù)實例繼承

// before 
function Person(name){
    this.name = name;
    this.say = function(){
        console.log('say hi');
    }
}
//after
function Person(name){
    this.name = name;
    this.age = 12;
}
Person.prototype.say = function(){
    console.log('say hi')
}
var son = new Person('king');
var daughter = new Person('kim');

isPrototypeOf()用來判斷,某個proptotype對象和某個實例之間的關(guān)系; Person.prototype.isPrototypeOf(son) // true hasOwnProperty()判斷某一個屬性到底是本地屬性卷仑,還是繼承自prototype對象的屬性; son.hasOwnProperty("age") //false in運算符還可以用來遍歷某個對象的所有屬性,包含繼承的屬性 for(var prop in son)

原型峻村,原型鏈

  • 內(nèi)置對象:Object、Function都是js內(nèi)置的函數(shù), 類似的還有我們常用到的Array系枪、RegExp雀哨、Date、Boolean私爷、Number雾棺、String

  • js分為函數(shù)對象和普通對象,每個對象都有proto屬性衬浑,但是只有函數(shù)對象才有prototype屬性

  • 除了Object的原型對象(Object.prototype)的proto指向null捌浩,其他內(nèi)置函數(shù)對象的原型對象和自定義構(gòu)造函數(shù)的proto都指向Object.prototype, 因為原型對象本身是普通對象

function F(){};
F.prototype.__proto__ = Object.prototype;
Array.prtotype.__proto__ = Object.prototype;
Object.prototype.__proto__ = null;

F.__proto__ = Function.prototype;

var f = new F();
f.__proto__ = F.prototype;

繼承

  • 構(gòu)造函數(shù)繼承
function Person(name){
    this.name = name
}
function Son(name){
    Person.call(this,name)
}
var obj = new Son('king');
console.log(obj.name)  // 'king'
  • prototype模式

組合模式

function Person(age){
    this.age = age
}
function Son(age){
    this.name = 'king';
    Person.call(this,age);
}
// 改變Son的prototype指向Person的實例,那么Son的實例就可以繼承Person
Son.prototype = new Person();
console.log(Son.prototype.constructor == Person.prototype.constructor)  //true
// 避免繼承鏈混亂工秩,將Son.prototype對象的constructor改回Son
Son.prototype.constructor = Son;
var obj = new Son(12);
console.log(obj.age)  // 'king'
console.log(Person.prototype.isPrototypeOf(obj)) // true 同時繼承Person和Son
function Person(){

}
Person.prototype.age = 12;
function Son(name){
    this.name = name;
}
// Son.prototype指向Person.prototype
Son.prototype = Person.prototype;
console.log(Son.prototype.constructor == Person.prototype.constructor)  //true
// 然而也修改了Person.prototype.constructor為Son
Son.prototype.constructor = Son;
var obj = new Son('king');
console.log(obj.age)  // 'king'
console.log(Person.prototype.isPrototypeOf(obj)) // true 同時繼承Person和Son
Son.prototype.sex = 'male';
console.log(Person.prototype.sex);  // 'male'

差別:與前一種方法相比尸饺,這樣做的優(yōu)點是效率比較高(不用執(zhí)行和建立Person的實例了),比較省內(nèi)存助币。缺點是 Person.prototype和Son.prototype現(xiàn)在指向了同一個對象浪听,那么任何對Son.prototype的修改,都會反映到Person.prototype眉菱,再次改進如下迹栓,

利用空對象作為中介

function Person(){
    this.age = 12
}
Person.prototype.sex = 'male';
function Son(){
    this.name = 'king';
}
var F = function(){};
F.prototype = Person.prototype;
Son.prototype = new F();
Son.prototype.constructor = Son;
Son.uber = Person.prototype;
var obj = new Son();
console.log(obj.sex);  // 'male'

// 封裝一下
function extend(Child, Parent) {
  var F = function(){};
  F.prototype = Parent.prototype;
  Child.prototype = new F();
  Child.prototype.constructor = Child;
  Child.uber = Parent.prototype;
}
extend(Son,Parent);
var obj = new Son();
console.log(obj.sex);  // 'male'
  • 拷貝繼承

利用 for...in 拷貝Person.prototype上的所有屬性給Son.prototype,Son的實例就相當于繼承了Person.prototype的所有屬性以及Son的屬性

  • 非構(gòu)造函數(shù)繼承

1.json格式的發(fā)明人Douglas Crockford,提出了一個object()函數(shù)俭缓,即后來的內(nèi)置函數(shù)Object.create()

function object(o){
    var F = function(){};
    F.prototype = o;
    return new F();
}
var child = {
    name: 'child'
}
var parent = {
    name: 'parent'
}
var child = object(parent)
console.log(child.name);   // 'parent'

2.淺拷貝

var parent = {
    area: ['A','B']
}
function extendCopy(o){
    var c = {};
    for(var i in o){
        c[i] = o[i]
    }
    return c;
}
var child = extendCopy(parent);
child.area.push('C');
console.log(parent.area);  // ['A','B','C']

現(xiàn)象:當父對象的屬性值為數(shù)組或者對象時克伊,子對象改變那個屬性值,父對象也會隨之改變华坦,因此愿吹,子對象只是獲得了內(nèi)存地址,而不是真正的拷貝惜姐,extendCopy只能用作基本數(shù)據(jù)類型的拷貝犁跪,這也是早期jquery實現(xiàn)繼承的方式

3.深拷貝

var parent = {
    area: ['A','B']
}
 function deepCopy(p, c) {
    var c = c || {};
    for (var i in p) {
      if (typeof p[i] === 'object') {
        c[i] = (p[i].constructor === Array) ? [] : {};
        deepCopy(p[i], c[i]);
      } else {
         c[i] = p[i];
      }
    }
    return c;
  }
var child = deepCopy(parent);
child.area.push('C');
console.log(parent.area);  // ['A','B']
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末耘拇,一起剝皮案震驚了整個濱河市撵颊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惫叛,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逞刷,死亡現(xiàn)場離奇詭異嘉涌,居然都是意外死亡,警方通過查閱死者的電腦和手機夸浅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門仑最,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人帆喇,你說我怎么就攤上這事警医。” “怎么了坯钦?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵预皇,是天一觀的道長。 經(jīng)常有香客問我婉刀,道長吟温,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任突颊,我火速辦了婚禮鲁豪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘律秃。我一直安慰自己爬橡,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布棒动。 她就那樣靜靜地躺著糙申,像睡著了一般。 火紅的嫁衣襯著肌膚如雪迁客。 梳的紋絲不亂的頭發(fā)上郭宝,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音掷漱,去河邊找鬼粘室。 笑死,一個胖子當著我的面吹牛卜范,可吹牛的內(nèi)容都是我干的衔统。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼锦爵!你這毒婦竟也來了舱殿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤险掀,失蹤者是張志新(化名)和其女友劉穎沪袭,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體樟氢,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡冈绊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了埠啃。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片死宣。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖碴开,靈堂內(nèi)的尸體忽然破棺而出毅该,到底是詐尸還是另有隱情,我是刑警寧澤潦牛,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布眶掌,位于F島的核電站,受9級特大地震影響罢绽,放射性物質(zhì)發(fā)生泄漏畏线。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一良价、第九天 我趴在偏房一處隱蔽的房頂上張望寝殴。 院中可真熱鬧,春花似錦明垢、人聲如沸蚣常。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抵蚊。三九已至,卻和暖如春溯革,著一層夾襖步出監(jiān)牢的瞬間贞绳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工致稀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留冈闭,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓抖单,卻偏偏與公主長得像萎攒,于是被迫代替她去往敵國和親遇八。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355