摘要:上一篇你不知道的筆記寫(xiě)在前面這是年第一篇博客,回顧去年年初列的學(xué)習(xí)清單,發(fā)現(xiàn)僅有部分完成了。當(dāng)然,這并不影響年是向上的一年在新的城市穩(wěn)定連續(xù)堅(jiān)持健身三個(gè)月早睡早起游戲時(shí)間大大縮減,學(xué)會(huì)生活。
上一篇:《你不知道的javascript》筆記_this
寫(xiě)在前面這是2019年第一篇博客,回顧去年年初列的學(xué)習(xí)清單,發(fā)現(xiàn)僅有部分完成了。當(dāng)然,這并不影響2018年是向上的一年:在新的城市穩(wěn)定、連續(xù)堅(jiān)持健身三個(gè)月、早睡早起、游戲時(shí)間大大縮減,學(xué)會(huì)生活。根據(jù)上一年目標(biāo)完成情況,新一年的計(jì)劃將采取【敏捷迭代】的方式:制定大方向,可臨時(shí)更新小目標(biāo)的策略。哈哈,話不多說(shuō)..
在學(xué)習(xí)《javascript高級(jí)程序設(shè)計(jì)》這本書(shū)時(shí),關(guān)于對(duì)象和原型,之前寫(xiě)過(guò)下面幾篇文章,可作參考:
《javascript高級(jí)程序設(shè)計(jì)》筆記:對(duì)象數(shù)據(jù)屬性和訪問(wèn)器屬性
《javascript高級(jí)程序設(shè)計(jì)》筆記:創(chuàng)建對(duì)象
《javascript高級(jí)程序設(shè)計(jì)》筆記:原型圖解
《javascript高級(jí)程序設(shè)計(jì)》筆記:繼承
一、對(duì)象基本知識(shí) 1.1 數(shù)據(jù)屬性與訪問(wèn)屬性《javascript高級(jí)程序設(shè)計(jì)》筆記:對(duì)象數(shù)據(jù)屬性和訪問(wèn)器屬性,這篇文章對(duì)數(shù)據(jù)屬性和訪問(wèn)器屬性有基本的介紹了,下面會(huì)做出一點(diǎn)補(bǔ)充說(shuō)明:
(1)默認(rèn)值
通過(guò)非Object.defineProperty的方式聲明的屬性默認(rèn)值
var obj = { a: 1 }; Object.getOwnPropertyDescriptor(obj, "a"); /*{ value: 1, writable: true, enumerable: true, configurable: true }*/
通過(guò)Object.defineProperty的方式聲明的屬性默認(rèn)值
var obj = {}; Object.defineProperty(obj, "a", {}); Object.getOwnpropertyDescriptor(obj, "a"); /*{ value: undefined, writable: false, enumerable: false, configurable: false }*/
(2)屬性configurable
configurable屬性設(shè)為false為不可逆過(guò)程
configurable:false其他屬性無(wú)法修改同時(shí)會(huì)禁止刪除該屬性
特例:configurable: false時(shí),writable的狀態(tài)可由true改為false,但不能由false變?yōu)?b>true
var obj = {}; Object.defineProperty(obj, "a", { configurable: false, writable: true, value: 1 })// {a: 1} Object.defineProperty(obj, "a", { configurable: false, writable: false, value: 2 })// {a: 2} Object.defineProperty(obj, "a", { configurable: false, writable: false, value: 3 })// Uncaught TypeError: Cannot redefine property: a
(3)區(qū)分?jǐn)?shù)據(jù)屬性和訪問(wèn)屬性
當(dāng)我們同時(shí)定義這兩個(gè)屬性時(shí),會(huì)提示錯(cuò)誤:
var obj = { _a: 1 }; Object.defineProperty(obj, "a", { configurable: true, enumerable: true, writable: true, value: 1, get() { return this._a; }, set(newVal) { this._a = newVal * 10; } }); Object.getOwnPropertyDescriptor(obj, "a"); // Uncaught TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute
因此:這兩個(gè)屬性不能同時(shí)存在,根據(jù)他們特有的屬性反過(guò)來(lái)判斷究竟是哪種屬性即可
(4)屬性enumerable: false的對(duì)象如何遍歷
可通過(guò)Object.getOwnPropertyNames獲取key,再遍歷
var obj = {}; Object.defineProperty(obj, "a", {value: 10}); // 一般的遍歷無(wú)法獲取 for(let k in obj){ console.log(k) }// undefined Object.getOwnPropertyNames(obj); // ["a"]1.2 不變性
(1)對(duì)象常量
創(chuàng)建方式:數(shù)據(jù)屬性:wraitable:false,configurable:false
特點(diǎn):不可修改/重定義/刪除
var obj = {}; Object.defineProperty(obj, "MY_CONSTANT", { value: 10, writable: false, configurable: false, }) obj.MY_CONSTANT = 11; console.log(obj.MY_CONSTANT); // 非嚴(yán)格模式下為10,嚴(yán)格模式報(bào)錯(cuò)
同樣的,如果這個(gè)對(duì)象常量是對(duì)象
var obj = {}; Object.defineProperty(obj, "MY_CONSTANT_OBJ", { value: {a: 1}, writable: false, configurable: false, }) obj.MY_CONSTANT_OBJ.b = 2; console.log(obj.MY_CONSTANT_OBJ); // {a: 1, b: 2}
因此:和const定義的普通常量類似,只要對(duì)象的地址不變便不會(huì)報(bào)錯(cuò)
(2)禁止擴(kuò)展
創(chuàng)建方式:Object.preventExtensions()
特點(diǎn):不可添加新屬性(可刪除可修改可重定義)
判斷:Object.isExtensible()不可擴(kuò)展返回false
var obj = {a: 1, b: 2}; Object.preventExtensions(obj); obj.c = 3; // {a: 1, b: 2} 不可添加 obj.a = 2; // {a: 2, b: 2} 可修改 delete obj.a; // {b: 2} 可刪除 Object.defineProperty(obj, "b", { configurable: false, value: 3 }); delete obj.b; // {b: 3} 可重定義 Object.isExtensible(obj); // false
(3)密封
創(chuàng)建方式:Object.seal()
特點(diǎn):不可添加/修改/刪除
判斷:Object.isSealed()密封時(shí)返回true
密封是在禁止擴(kuò)展的基礎(chǔ)上禁止刪除和重定義。相當(dāng)于把現(xiàn)有屬性標(biāo)記為configurable:false
var obj = {a: 1, b: 2}; Object.seal(obj); obj.c = 3; // {a: 1, b: 2} 不可添加 delete obj.a; // {a: 1, b: 2} 不可刪除 // configurable:false 不可重定義 obj.a = 2; // {a: 2, b: 2} 可修改 Object.isSealed(obj); // true
(4)凍結(jié)
創(chuàng)建方式:Object.freeze()
特點(diǎn):最高級(jí)別的不可變
判斷:Object.isFrozen()
凍結(jié)是在密封的基礎(chǔ)上把現(xiàn)有屬性標(biāo)記為writable:false,所有屬性相當(dāng)于常量屬性
1.3 存在性in操作符:檢查屬性是否在對(duì)象及其原型鏈中
hasOwnProperty:僅檢查屬性是否在該對(duì)象上
因此,可結(jié)合二者來(lái)判斷屬性是否僅存在與原型鏈上
function hasPrototypeProperty(obj, property) { return (property in obj) && !(obj.hasOwnProperty(property)); }
另外,可獲取的某個(gè)對(duì)象所有key(未遍歷至原型鏈),再判斷指定屬性是否在對(duì)象內(nèi);
遍歷方法:
Object.keys(); Object.getOwnPropertyNames(); // 可獲取不可枚舉的屬性1.4 深淺拷貝
理解深淺拷貝需要對(duì)內(nèi)存有一定理解,如果對(duì)基礎(chǔ)類型和復(fù)雜類型(值類型和引用類型)在內(nèi)存中的區(qū)別仍沒(méi)有清楚的認(rèn)識(shí),可參考《javascript高級(jí)程序設(shè)計(jì)》筆記:值類型與引用類型這篇文章,否則忽略即可
值類型數(shù)據(jù)是直接存在于棧內(nèi)存中,復(fù)制操作是直接開(kāi)辟內(nèi)存并存值;
引用類型數(shù)據(jù)棧內(nèi)存中存儲(chǔ)的只是堆內(nèi)存中真實(shí)數(shù)據(jù)的一個(gè)引用,復(fù)制操作僅復(fù)制引用,并未真實(shí)復(fù)制堆中的數(shù)據(jù);
根據(jù)上面的認(rèn)識(shí),我們思考一下:什么是深拷貝,什么是淺拷貝?
深淺拷貝僅僅是針對(duì)引用類型而言,深拷貝是按照目標(biāo)對(duì)象的結(jié)構(gòu)復(fù)制一份完全相同的對(duì)象,新的對(duì)象與原對(duì)象各個(gè)嵌套層級(jí)上內(nèi)存完全獨(dú)立,修改新對(duì)象不會(huì)更改原對(duì)象;引用類型的拷貝中除深拷貝外的均為淺拷貝(不完全深拷貝)
本文僅介紹我在項(xiàng)目中經(jīng)常使用的深淺拷貝幾種方式,不對(duì)底層實(shí)現(xiàn)探究:
擴(kuò)展運(yùn)算符【淺】
var obj = {a: 1, b: 2}; var arr = [1,2,3,4]; // 對(duì)象 var copyObj = {...obj}; copyObj === obj; // false // 數(shù)組 var copyArr = [...arr]; copyArr === arr; // false
Object.assign()【淺】
// 對(duì)象 var copyObj = Object.assign({}, obj); copyObj === obj; // false // 數(shù)組 var copyArr = Object.assign([], arr); copyArr === arr; // false
數(shù)組也是對(duì)象,因此Object.assign也可使用,只是一般不這么用且有更簡(jiǎn)單的方式
數(shù)組拷貝實(shí)現(xiàn)【淺】
var copyArr1 = arr.slice(); var copyArr2 = arr.concat();
lodash中的clone/cloneDeep【淺/深】
工具庫(kù)lodash中提供了深淺拷貝的方法,簡(jiǎn)單易用且能夠按需引入
// 全部引入 import _ from "lodash"; // _.clone() _.cloneDeep() // 按需引入 import clone from "lodash/clone"; import cloneDeep from "lodash/cloneDeep";
JSON方式【深】
這個(gè)是平時(shí)項(xiàng)目中最常用的深拷貝方式,局限性就是,無(wú)法拷貝方法
JSON.parse(JSON.stringify(obj));
其實(shí),深拷貝就是通過(guò)遞歸逐級(jí)淺拷貝實(shí)現(xiàn)的,因?yàn)閷?duì)于復(fù)雜類型的元素均為值類型的淺拷貝便是深拷貝。例:[1,2,3].slice()便是深拷貝
如需更深入,可參考:JavaScript 淺拷貝與深拷貝
二、原型&繼承類/繼承描述了一種代碼的組織結(jié)構(gòu)形式——一種在軟件中對(duì)真實(shí)世界中問(wèn)題領(lǐng)域的建模方法
下面會(huì)對(duì)這種建模慢慢闡述
2.1 原型非常慶幸,之前寫(xiě)過(guò)《javascript高級(jí)程序設(shè)計(jì)》筆記:原型圖解的文章得到了許多朋友的認(rèn)可。關(guān)于原型的一些知識(shí)這里面也說(shuō)的七七八八了,讀完《你不知道的javascript》后再做些許補(bǔ)充
下面搬出經(jīng)典的鐵三角鎮(zhèn)樓,在原型圖解中已做說(shuō)明,在此不嘮述
(1)原型有什么用
為什么要抽離出原型的概念?在js中原型就相當(dāng)于類;類有什么作用呢?
書(shū)中有一個(gè)恰當(dāng)?shù)谋扔鳎?strong>類相當(dāng)于建造房子時(shí)的藍(lán)圖,實(shí)例相當(dāng)于我們需要真實(shí)建造的房子
這個(gè)藍(lán)圖(類)抽離出了房子的諸多特性(屬性),如寬/高/占地/窗戶數(shù)量/材料等等。我們建造房子(創(chuàng)建實(shí)例)時(shí)只需要按照這些搭建即可,而不是從零開(kāi)始
回到編程中:有了類的概念,針對(duì)一些具有公共的屬性和方法對(duì)象,我們可以將其抽離出來(lái),以便下次使用,簡(jiǎn)化我們構(gòu)建的過(guò)程
這個(gè)是js中數(shù)組的【類】,實(shí)例以后就能夠直接使用,而無(wú)需將公用的方法定義在實(shí)例上
(2)屬性屏蔽規(guī)則
屬性查找規(guī)則:沿著原型鏈查找,到最近的對(duì)象截止,若一直到Object.prototype也無(wú)法找到,則返回undefined;
反言之,屬性的屏蔽規(guī)則亦是如此?原型鏈上,同名屬性靠前會(huì)屏蔽掉后面的同名屬性?答案遠(yuǎn)沒(méi)有這么簡(jiǎn)單,分為三種情況考慮:(以myObject.foo = "bar";為例)
1. 如果在原型鏈上層存在名為foo的普通數(shù)據(jù)訪問(wèn)屬性并且沒(méi)有被標(biāo)記為只讀writable:true,那么直接在myObject中添加一個(gè)名為foo的新屬性
function Fn() {} Fn.prototype.foo = "this is proto property"; var myObject = new Fn(); myObject.foo = "this is my own property"; myObject.foo; // "this is my own property";
2. 如果原型鏈上層存在foo,但是被標(biāo)記為只讀writable:false,那么無(wú)法修改已有屬性或者在myObject中創(chuàng)建屏蔽屬性
function Fn() {} Object.defineProperty(Fn.prototype, "foo", { value: "this is proto property", writable: false, }); var myObject = new Fn(); myObject.foo = "this is my own property"; myObject.foo; // "this is proto property";
3. 如果在原型鏈上層存在foo并且他是一個(gè)setter,那么一定會(huì)調(diào)用這個(gè)setter
function Fn() {} Object.defineProperty(Fn.prototype, "foo", { set(newValue) { this._foo = "haha! this is setter prototype"; }, get() { return this._foo; } }); var myObject = new Fn(); myObject.foo = "this is my own property"; myObject.foo; // "haha! this is setter prototype";
(3)一個(gè)面試題
var anotherObject = { a: 2 }; var myObject = Object.create(anotherObject); // node1 myObject.a++; anotherObject.a; // ? myObject.a; // ? // node2
上面問(wèn)號(hào)處輸出值為多少?
分析:
node1和node2處分別執(zhí)行下面的代碼并輸出
// node1 anotherObject.hasOwnProperty("a"); // true myObject.hasOwnProperty("a"); // false // node2 anotherObject.hasOwnProperty("a"); // true myObject.hasOwnProperty("a"); // true
看到了什么,執(zhí)行完myObject.a++;后實(shí)例對(duì)象創(chuàng)建了一個(gè)自己的屬性;為什么?自增操作相當(dāng)于myObject.a = myObject.a + 1首先查找到屬性,后在實(shí)例對(duì)象上創(chuàng)建一個(gè)新的同名屬性,屏蔽原型上的屬性;
答案:2 3
《javascript高級(jí)程序設(shè)計(jì)》筆記:繼承這篇文章分析了各種繼承的情況,一步一步演化至更精致的繼承情況
(1)繼承有什么用
需求:為我的汽車建一個(gè)對(duì)象
以前:{id: XX, 牌照: XX, 品牌: XX, 油耗: XX, 載人: XX, 顏色: XX .. }
建模的思想來(lái)搭建類和繼承體系:
抽離交通工具的類 Vehicle = {油耗: XX, 載人: XX, 顏色: XX, drive()}
抽離汽車的類 Car = {繼承Vehicle, 品牌: XX }
實(shí)例化我的汽車 {繼承Car, id: XX, 牌照: XX}
抽離類并繼承,加上實(shí)例對(duì)象特有的屬性便能夠具體某一對(duì)象,達(dá)到高效利用的目的
(2)新的繼承
下面的方式便是在“組合繼承”的進(jìn)一步“精致”,當(dāng)然還有class語(yǔ)法糖(后續(xù)計(jì)劃..)
function Foo(name) { this.name = name; } Foo.prototype.sayName = function() { console.log(this.name); } var foo = new Foo("xiaoming"); console.log(foo) function Bar(name, id) { Foo.call(this, name) this.id = id; } // Object.create()方式 Bar.prototype = Object.create(Foo.prototype); // Object.setPrototypeOf()方式 // Object.setPrototypeOf( Bar.prototype, Foo.prototype ); Bar.prototype.sayId = function() { console.log(this.id); } var bar = new Bar("xiaofeng", 1234) console.log(bar);
兩者對(duì)比而言,顯然Object.setPrototypeOf()方式更加完備一些(無(wú)需再次聲明constructor)
2.3 原型本質(zhì)【行為委托】對(duì)象之間并非從屬關(guān)系,而是委托關(guān)系,js中委托關(guān)系正是通過(guò)Object.create()完成Object.create()方法創(chuàng)建一個(gè)新對(duì)象,使用現(xiàn)有的對(duì)象來(lái)提供新創(chuàng)建的對(duì)象的__proto__
在上面的繼承中,我們已經(jīng)看到了Object.create()的身影;下面來(lái)對(duì)比類和委托思維模型
// 類/繼承模型 function Foo(who) { this.me = who; } Foo.prototype.identify = function() { return "I am " + this.me; }; function Bar(who) { Foo.call( this, who ); } Bar.prototype = Object.create( Foo.prototype ); Bar.prototype.speak = function() { alert( "Hello, " + this.identify() + "." ); }; var b1 = new Bar( "b1" ); var b2 = new Bar( "b2" ); b1.speak(); b2.speak();
// 委托模型 Foo = { init(who) { this.me = who; }, identify() { return "I am " + this.me; } }; Bar = Object.create( Foo ); Bar.speak = function() { alert( "Hello, " + this.identify() + "." ); }; var b1 = Object.create( Bar ); b1.init( "b1" ); var b2 = Object.create( Bar ); b2.init( "b2" ); b1.speak(); b2.speak();
類風(fēng)格的代碼強(qiáng)調(diào)的是實(shí)體與實(shí)體之間的關(guān)系,委托風(fēng)格的代碼強(qiáng)調(diào)的是對(duì)象之間的關(guān)聯(lián)關(guān)系;
如何選用?像上面章節(jié)舉的例子:交通工具-->汽車-->具體某個(gè)汽車的關(guān)系,選用類;沒(méi)有太大關(guān)聯(lián)的對(duì)象可直接用委托實(shí)現(xiàn)
上一篇:《你不知道的javascript》筆記_this
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/100761.html
下一篇:《你不知道的javascript》筆記_對(duì)象&原型 寫(xiě)在前面 上一篇博客我們知道詞法作用域是由變量書(shū)寫(xiě)的位置決定的,那this又是在哪里確定的呢?如何能夠精準(zhǔn)的判斷this的指向?這篇博客會(huì)逐條闡述 書(shū)中有這樣幾句話: this是在運(yùn)行時(shí)進(jìn)行綁定的,并不是在編寫(xiě)時(shí)綁定,它的上下文取決于函數(shù)調(diào)用時(shí)的各種條件this的綁定和函數(shù)聲明的位置沒(méi)有任何關(guān)系,只取決于函數(shù)的調(diào)用方式當(dāng)一個(gè)函數(shù)被調(diào)用時(shí)...
摘要:基礎(chǔ)原型原型鏈構(gòu)造函數(shù)默認(rèn)有這一行張三李四構(gòu)造函數(shù)擴(kuò)展其實(shí)是的語(yǔ)法糖其實(shí)是的語(yǔ)法糖其實(shí)是使用判斷一個(gè)函數(shù)是否是一個(gè)變量的構(gòu)造函數(shù)原型規(guī)則和示例所有的引用類型數(shù)組對(duì)象函數(shù),都具有對(duì)象屬性即可自有擴(kuò)展的屬性,除外所有的引用類型數(shù)組對(duì)象函數(shù), JavaScript基礎(chǔ) —— 原型&&原型鏈 構(gòu)造函數(shù) function Foo(name, age) { this.name = na...
摘要:然而事實(shí)上并不是。函數(shù)本身也是一個(gè)對(duì)象,但是給這個(gè)對(duì)象添加屬性并不能影響。一圖勝千言作者給出的解決方案,沒(méi)有麻煩的,沒(méi)有虛偽的,沒(méi)有混淆視線的,原型鏈連接不再赤裸裸。所以是這樣的一個(gè)函數(shù)以為構(gòu)造函數(shù),為原型。 注意:本文章是個(gè)人《You Don’t Know JS》的讀書(shū)筆記。在看backbone源碼的時(shí)候看到這么一小段,看上去很小,其實(shí)忽略了也沒(méi)有太大理解的問(wèn)題。但是不知道為什么,我...
摘要:初識(shí)在中有兩種特別的基本數(shù)據(jù)類型初學(xué)者對(duì)其也很模糊或者直接認(rèn)為它倆相等。作為函數(shù)參數(shù),表示該函數(shù)的參數(shù)不是對(duì)象對(duì)象原型鏈的終點(diǎn)。對(duì)象屬性沒(méi)有賦值,該屬性為當(dāng)函數(shù)沒(méi)有返回值時(shí),默認(rèn)返回第一次分享文章,如有錯(cuò)誤請(qǐng)斧正 1.初識(shí) null & undefined 在javascript 中有兩種特別的基本數(shù)據(jù)類型 null undefined 初學(xué)者 對(duì)其也很模糊或者直接認(rèn)為它倆相等。 確實(shí)...
摘要:如果存在于原型鏈上層,賦值語(yǔ)句的行為就會(huì)有些不同。中包含的屬性會(huì)屏蔽原型鏈上層的所有屬性,因?yàn)榭偸菚?huì)選擇原型鏈中最底層的屬性。如果不直接存在于中而是存在于原型鏈上層時(shí)會(huì)出現(xiàn)的三種情況。類構(gòu)造函數(shù)原型函數(shù),兩個(gè)函數(shù)通過(guò)屬性和屬性相關(guān)聯(lián)。 1 [[Prototype]] 對(duì)于默認(rèn)的 [[Get]] 操作來(lái)說(shuō),如果無(wú)法在對(duì)象本身找到需要的屬性,就會(huì)繼續(xù)訪問(wèn)對(duì)象的 [[Prototype]] ...
閱讀 4758·2021-11-15 11:39
閱讀 2700·2021-11-11 16:55
閱讀 2208·2021-10-25 09:44
閱讀 3512·2021-09-22 16:02
閱讀 2444·2019-08-30 15:55
閱讀 3132·2019-08-30 13:46
閱讀 2674·2019-08-30 13:15
閱讀 1959·2019-08-30 11:12