javascript
对象认知全提升,成为 JS 高手
1.對象屬性
常規(guī)屬性
- 鍵為字符串的屬性
- 根據(jù)創(chuàng)建時的順序排序
執(zhí)行結(jié)果:
排序?qū)傩?/h3>
- 屬性鍵值為數(shù)字或者數(shù)字字符串的屬性
- 按照索引值大小升序排序
const obj = {};obj[1] = "p1";
obj[6] = "p6";
obj[2] = "p2";
//obj["1"] = "p1";
//obj["6"] = "p6";
//obj["2"] = "p2";for (const p in obj) {console.log("property:", p);
}
執(zhí)行結(jié)果:
同時存在先輸出排序?qū)傩?/p> const obj = {};obj.p1 = "str1"; obj.p6 = "str6"; obj.p2 = "str2";obj[1] = "num1"; obj[6] = "num6"; obj[2] = "num2";for (let p in obj) {console.log("property:", obj[p]); }
執(zhí)行結(jié)果:
為什么要設(shè)計常規(guī)屬性和排序?qū)傩?/p>
- 使用兩種線性結(jié)構(gòu)保存(elements、properties),提升V8引擎屬性的訪問速度
2.屬性來源
- 靜態(tài)屬性,例如: Object.assign
- 原型屬性,例如: Object.prototype.toString
- 實例屬性,例如: function Person (name){ this.name = name }
3.屬性描述符
- Object.defineProperty 、 Object.defineProperties 設(shè)置屬性信息
- Object.getOwnPropertyDescriptor 、Object.getOwnPropertyDescriptors 獲取屬性描述信息* configurable:可配置(屬性能不能被刪除和重新通過 defineProperty 設(shè)置,但是當(dāng)設(shè)置 writable 和 value 從 true 到 false 則是允許的)* enumerable:是否可枚舉* value:值* writable:是否可被更改* set:訪問器函數(shù)* get:訪問器函數(shù)
- 數(shù)據(jù)屬性: value + writable + configurable + enumerable
- 訪問器屬性:get + set + configurable + enumerable
默認(rèn) defineProperty 不傳第三個描述符
const obj = {};Object.defineProperty(obj, "name", {});console.log(obj.name); // undefined 且不能被改寫console.log(Object.getOwnPropertyDescriptor(obj, "name"));執(zhí)行結(jié)果如下:
Object.defineProperty的缺點
- 無法監(jiān)聽數(shù)組變化
- 只能劫持對象的屬性,因此我們需要對對象的每個屬性進(jìn)行遍歷。如果屬性也是對象,還得進(jìn)行遞歸
4.對象限制
1.對象可擴(kuò)展-Object.preventExtensions
- Object.preventExtensions:對象變的不可擴(kuò)展,也就是永遠(yuǎn)不能再添加新的屬性
- Object.isExtensible:判斷一個對象是否是可擴(kuò)展
2.對象的封閉-Object.seal
- Object.seal:阻止添加新屬性+屬性標(biāo)記為不可配置
- Object.isSealed:檢查一個對象是否被密封
3.對象的凍結(jié)- Object.freeze
- Object.freeze:不加新屬性+不可配置+不能修改值
- Object.isFrozen:檢查一個對象是否被凍結(jié)
4.總結(jié)
| Object.preventExtensions | x | √ | √ | √ |
| Object.seal | x | x(修改 writable 為 false 可以) | x | √ |
| Object.freeze | x | x(修改 writable 為 false 可以) | x | x |
5.訪問對象原型
1.prototype
- prototype是一個對象
- 原型會形成原型鏈,原型鏈上查找屬性比較耗時,訪問不存在的屬性會訪問整個原型鏈
2._proto_
- 構(gòu)造函數(shù)的原型
- null以外的對象均有 _proto_ 屬性
- Function 、class的實例有 prototype 以及 _proto_ 屬性
- 普通函數(shù),祖上第三代上必為null
3.instanceof
- 檢測構(gòu)造函數(shù)(右側(cè))的prototype屬性是否出現(xiàn)在某個實例對象(左側(cè))的原型鏈上
- Object instanceof Function 、 Function instanceof Object
手寫instanceof
function instanceOf(instance, cclass) {let proto = instance.__proto__;let prototype = cclass.prototype;while (proto) {if (proto === prototype) return true;proto = proto.__proto__;}return false; }class Parent {} class Child extends Parent {} class CChild extends Child {} class Luren {} const cchild = new CChild();console.log(instanceOf(cchild, Parent)); // true console.log(instanceOf(cchild, Child)); // true console.log(instanceOf(cchild, CChild)); // true console.log(instanceOf(cchild, Object)); // true console.log(instanceOf(cchild, Date)); // false console.log(instanceOf(cchild, Luren)); // false4.getPrototypeOf
- Object.getPrototypeof() 、 Reflect.getPrototypeOf()* 返回對象的原型
- 內(nèi)部先toObject轉(zhuǎn)換,注意null和undefined沒有原型
5.setPrototypeOf
- Object.setPrototypeof() , Reflect.setPrototypeOf()* 指定對象的原型
- 原型的盡頭是null
6.isPrototypeOf
- Object.isPrototypeof 、 Object.prototype.isPrototypeof 、 Reflect.isPrototypeOf 、 Function.isPrototypeOf* 一個對象是否存在于另一個對象的原型鏈上
7.Object.create
- 使用現(xiàn)有的對象來提供新創(chuàng)建的對象的 __proto__
- 使用 Object.create(null) 可以創(chuàng)建一個沒有原型的純凈對象
6.對象屬性的遍歷
屬性分類:
- 普通屬性
- 原型屬性
- Symbol屬性
- 不可枚舉的屬性
遍歷方法:
| for in | √ | x | x | √ |
| Obiect.keys | √ | x | x | x |
| Object.getOwnPropertyNames | √ | √ | x | x |
| Object.getOwnPropertySymbols | x | √ | √ | x |
| Reflect.ownKeys | √ | √ | √ | x |
1.獲取非原型屬性
- Reflect.ownKeys = Object.getOwnPropertyNames + Object.getOwnPropertySymbols
2.獲取原型上所有屬性
- Reflect.ownKeys + 遞歸原型鏈
執(zhí)行結(jié)果如下:
3.獲取所有不可枚舉的屬性
const symbolSalary = Symbol.for("ins_symbol_attr_salary");function Person(age, name) {this.ins_in_attr_age = age;this.ins_in_attr_name = name; }const person = new Person(100, "程序員");//Symbol 屬性 person[symbolSalary] = 6000; person["ins_no_enumerable_attr_sex"] = "男";// sex 不可枚舉 Object.defineProperty(person, "ins_no_enumerable_attr_sex", {enumerable: false, });Object.defineProperty(person, symbolSalary, {enumerable: false,value: 999, });// function getNoEnumerable(_obj) {//獲取原型對象const keys = Reflect.ownKeys(_obj);// const result = keys.filter(key=> {// return !Object.getOwnPropertyDescriptor(_obj, key).enumerable// })// return result;const result = keys.filter((key) => {return !Object.prototype.propertyIsEnumerable.call(_obj, key);});return result; }console.log(getNoEnumerable(person));執(zhí)行結(jié)果如下:
7.對象隱式轉(zhuǎn)換和注意事項
1.顯示轉(zhuǎn)換
- 通過 JS 轉(zhuǎn)換方法進(jìn)行轉(zhuǎn)換、
- 比如 String 、 Number 、 parselnt/parseFloat 等
2.隱式轉(zhuǎn)換
- 編譯器自動完成類型轉(zhuǎn)換的方式就稱為隱式轉(zhuǎn)換,往往預(yù)期和傳入不一致往往就會發(fā)生* 二元 + 運算符(類型不一樣的相加)* 關(guān)系運算符 >、<、 >=、<=、==* 邏輯! 、if/while ,三目條件* 屬性鍵的遍歷、for in等* 模板字符串
3.對象隱式轉(zhuǎn)換規(guī)則
涉及到三個方法
- Symbol.toPrimitive* Object.prototype.valueOf* Object.prototype.toString* 如果[Symbol.toPrimitive](hint)方法存在,優(yōu)先調(diào)用,無視valueOf和toSting方法* 否則,如果期望是"string" ——先調(diào)用obj.toString()如果返回不是原始值,繼續(xù)調(diào)用obj.valueOf()* 否則,如果期望是"number"或"default" 先調(diào)用 obj.valueOf() 如果返回不是原始值,繼續(xù)調(diào)用obj.toString()如果未定義[Symbol.toPrimitive](hint),期望string,此時toString()和valueOf()都沒有返回原始值會拋出異常
4.Symbol.toPrimitive(hint)
- hint - “string”
- hint - “number”
- hint - “default”
hint - “string”
- window.alert(obj)
- 模板字符串`${obj}
- test[obj]=123
執(zhí)行結(jié)果:
hint - “number”
- 一元+,位移
- -、*、/ 等關(guān)系運算符
- Math.pow、String、prototype.slice 等很多內(nèi)部方法
執(zhí)行結(jié)果如下:
hint - “default”
- 二元+
- == 、!=
5.ValueOf 和 toString
來幾個小練習(xí)大家自己想想
const user = {name: "John",age: 10,toString() {return this.name;},valueOf() {return this.age;}, };console.log("user:", +user); // user: 10 console.log("user:", `${user}`); // user: John const user = {name: "John",age: 10,toString() {return this.name;},valueOf() {return this;}, };console.log("user:", +user); // NaN // 相當(dāng)于 console.log(+"John"); // NaN const user = {name: "John",age: 10,toString() {return this;},valueOf() {return this.age;}, };Object.prototype.toString = undefined;console.log("user:", `${user}`); // user: 10 const obj = {value: 10,toString: function () {return this.value + 10;},valueOf: function () {return this.value;}, };obj[obj] = obj.value;console.log("keys:", Object.keys(obj)); // keys: [ '20', 'value', 'toString', 'valueOf' ] console.log("${obj}:", `${obj}`); // ${obj}: 20 console.log("obj + 1:", obj + 1); // obj + 1: 11 console.log('obj + "":', obj + ""); // obj + "": 10特殊Date
- hint是default ,是優(yōu)先調(diào)用toString,然后調(diào)用valueOf
執(zhí)行結(jié)果如下:
8.JSON和toJSON
- 嚴(yán)格意義上JSON對象是不合理,JSON是文本協(xié)議
- 全局作用域下JSON,名為JSON,是Object對象
- JSON是一種輕量級的、基于文本的、與語言無關(guān)的語法,用于定義數(shù)據(jù)交換格式
- 它來源于ECMAScript編程語言,但是獨立于編程語言
JSON特征
-
JSON就是一串字符串,使用特定的符號標(biāo)注* {}雙括號表示對象* []中括號表示數(shù)組* ""雙引號內(nèi)是屬性鍵或值?### 屬性鍵
-
只能是字符串
-
必須雙引號包裹
JSON值
- object
- array
- number(只能十進(jìn)制)
- string
- true
- false
- null
合格JSON
`["你", "我", "她"]``{ "name": "云牧", "age": 18 }``{ "IDS": ["123", "456"] }``{ "name": null }``{}` `[]`不合格JSON
` {"name":"云牧",[Symbol.for("sex")]: 1 }`` { name: "云牧", 'age': 32} `` {"name": "云牧","age": undefined }``[-10, 0xDDFF]` ` { "name": "云牧","created": new Date(),"price": 18"getPrice": function() { return this.price;} }`` { "name":"云牧", "age": 32, } `JSON.stringify()
- 語法:JSON.stringify(value[, replacer [, space]])
- 第二個參數(shù)replacer:過濾屬性或者處理值* 如果該參數(shù)是一個數(shù)組:則只有包含在這個數(shù)組中的屬性名才會被序列化到最終的JSON字符串中* 如果該參數(shù)是一個函數(shù)︰則在序列化過程中,被序列化的值的每個屬性都會經(jīng)過該函數(shù)的轉(zhuǎn)換和處理* 如果該參數(shù)為null或者未提供:,則對象所有的屬性都會被序列化
- 第三個參數(shù)space:美化輸出格式
序列化undefined、任意的函數(shù)、symbol
- 作為對象屬性值,自動忽略
- 作為數(shù)組,序列化返回null
- 單獨序列化時,返回undefined
其他規(guī)則
- Date返回 ISO 字符串
- 循環(huán)引用報錯
- NaN、Infinity、null都會作為null
- Biglnt報錯
- Map、Set、WeakMap等對象,僅序列化可枚舉屬性
JSON.parse()
- 注意:第二個參數(shù)函數(shù)reviver ( k, v )* k代表屬性鍵,v代表屬性值,如果返回undefined則會從當(dāng)前的屬性刪除
注意:遍歷順序
const jsonStr = `{"name": "牙膏","count": 10, "orderDetail": {"createTime": 1632996519781,"orderId": 8632996519781,"more": {"desc": "描述"}} }`;JSON.parse(jsonStr, function (k, v) {console.log("key:", k);return v; });執(zhí)行結(jié)果如下:
注意:this
const jsonStr = `{"name": "云牧","orderDetail": {"createTime": 1632996519781} }`;JSON.parse(jsonStr, function (k, v) {console.log("key:", k, ",this:", this);return v; });執(zhí)行結(jié)果如下:
toJSON
- 對象擁有toJSON方法,toJSON會覆蓋對象默認(rèn)的序列化行為
使用場景
- 請求接口發(fā)送數(shù)據(jù),接收數(shù)據(jù)
- 本地存儲
- 深克隆對象
9.學(xué)習(xí)自檢
題目一
const obj = {},objA = { propertyA: "A" },objB = { propertyB: "B" };obj[objA] = "objectA"; obj[objB] = "ObjectB";for (let [p, v] of Object.entries(obj)) {console.log("p:", p, ", v:", v); }執(zhí)行結(jié)果:
- Object.entires :迭代器,能獲取鍵值對
- 對象鍵的特性∶本質(zhì)上是字符串,如果是數(shù)字,轉(zhuǎn)換字符串
- 隱式轉(zhuǎn)換︰對象的隱式轉(zhuǎn)換,Symbol.toPrimitive,valueOf,toString()
題目二
const person = {name: "二哈", }; const person2 = Object.create(person); delete person2.name;console.log(person2.name);執(zhí)行結(jié)果:
題目三
const val = (+{} + [])[+[]]; console.log(val);/*(+{} + [])[+[]] // +{}=> NaN (NaN + [])[+[]] // [] 隱式轉(zhuǎn)換 '' (NaN + '')[+[]] // NaN + '' => 'NaN' ('NaN')[+[]] // +[] => 0 ('NaN')[0] // 'N'*/題目四
const proto = {name: "原型",arr: [1, 2], }; const person = Object.create(proto); person.name = "實例"; person.arr.push(3);console.log(person.name); console.log(proto.name);console.log(person.arr); console.log(proto.arr);執(zhí)行結(jié)果:
題目五
const toString = Object.prototype.toString; function getObjectType(obj) {return toString.call(obj).slice(8, -1); } const obj = String.prototype; console.log(typeof obj); console.log(getObjectType(obj));執(zhí)行結(jié)果:
題目六
let a = { n: 1 }; a.x = a = { n: 2 };// 求a.x console.log(a.x);執(zhí)行結(jié)果:
題目七
const proto = {name: "p_parent",type: "p_object",[Symbol.for("p_address")]: "地球", };const ins = Object.create(proto); Object.defineProperty(ins, "age", {value: 18, }); ins.sex = 1; ins[Symbol.for("say")] = function () {console.log("say"); };const inKeys = []; for (let p in ins) {inKeys.push(p); }console.log(inKeys); console.log(Reflect.ownKeys(ins));執(zhí)行結(jié)果:
最后
整理了一套《前端大廠面試寶典》,包含了HTML、CSS、JavaScript、HTTP、TCP協(xié)議、瀏覽器、VUE、React、數(shù)據(jù)結(jié)構(gòu)和算法,一共201道面試題,并對每個問題作出了回答和解析。
有需要的小伙伴,可以點擊文末卡片領(lǐng)取這份文檔,無償分享
部分文檔展示:
文章篇幅有限,后面的內(nèi)容就不一一展示了
有需要的小伙伴,可以點下方卡片免費領(lǐng)取
總結(jié)
以上是生活随笔為你收集整理的对象认知全提升,成为 JS 高手的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 元分析 | 精神分裂症患者认知功能的脑结
- 下一篇: 专利下载方法及案例