摘要:客觀別急,今天真的是要說一個(gè),也許你早已知曉,也許你時(shí)常躺槍于他手,悄悄地,我們慢慢開始。使用案例源碼用到了前面已經(jīng)寫好的函數(shù),所以認(rèn)為獲取的屬性值,不包括原型返回一個(gè)副本,使其鍵和值對(duì)換。
前言
underscore.js源碼分析第四篇,前三篇地址分別是,如果你對(duì)這個(gè)系列感興趣,歡迎點(diǎn)擊watch,隨時(shí)關(guān)注動(dòng)態(tài)。
教你認(rèn)清這8大殺手锏
那些不起眼的小工具?
(void 0)與undefined之間的小九九
原文地址
源碼地址
逗我呢?哥!你要說什么bug,什么bug,什么bug,我最討厭bug。去他妹的bug。
客觀別急,今天真的是要說一個(gè)bug,也許你早已知曉,也許你時(shí)常躺槍于他手,悄悄地,我們慢慢開始。
for in 遍歷對(duì)象屬性時(shí)存在bug
for in 遍歷對(duì)象屬性時(shí)存在bug
for in 遍歷對(duì)象屬性時(shí)存在bug
使用for in去遍歷一個(gè)對(duì)象俺們?cè)偈煜げ贿^了,經(jīng)常干這種事,那他到底可以遍歷一個(gè)對(duì)象哪些類型的屬性呢? 長(zhǎng)得帥的還是看起來美美的,瞎說,它能夠遍歷的是對(duì)象身上那些可枚舉標(biāo)志([[Enumerable]])為true的屬性。
對(duì)于通過直接的賦值和屬性初始化的屬性,該標(biāo)識(shí)值默認(rèn)為即為 true
對(duì)于通過 Object.defineProperty 等定義的屬性,該標(biāo)識(shí)值默認(rèn)為 false
舉個(gè)例子哪些屬性可以被枚舉
let Person = function (name, sex) { this.name = name this.sex = sex } Person.prototype = { constructor: Person, showName () { console.log(this.name) }, showSex () { console.log(this.sex) } } Person.wrap = { sayHi () { console.log("hi") } } var p1 = new Person("qianlongo", "sex") p1.sayBye = () => { console.log("bye") } p1.toString = () => { console.log("string") } Object.defineProperty(p1, "info", { enumerable: false, configurable: false, writable: false, value: "feDev" });? for (var key in p1) { console.log(key) } // name // sex // sayBye // constructor // showName // showSex // toString
可以看到我們手動(dòng)地用defineProperty,給某個(gè)對(duì)象設(shè)置屬性時(shí),enumerable為false此時(shí)該屬性是不可枚舉的
Person繼承自O(shè)bject構(gòu)造函數(shù),但是for in并沒有枚舉出Object原型上的一些方法
手動(dòng)地覆蓋對(duì)象原型上面的方法toString也是可枚舉的
如何判斷一個(gè)對(duì)象的屬性是可枚舉的方式其實(shí)很簡(jiǎn)單,使用原生js提供的Object.propertyIsEnumerable來判斷
let obj = { name: "qianlongo" } let obj2 = { name: "qianlongo2", toString () { return this.name } } obj.propertyIsEnumerable("name") // true obj.propertyIsEnumerable("toString") // false obj2.propertyIsEnumerable("name") // true obj2.propertyIsEnumerable("toString") // true
為什么obj判斷toString為不可枚舉屬性,而obj2就是可枚舉的了呢?原因很簡(jiǎn)單,obj2將toString重寫了,而一個(gè)對(duì)象自身直接賦值的屬性是可被枚舉的
說了這么多,接下來我們來看一下下劃線中涉及到遍歷的部分對(duì)象方法,come on!!!
_.has(object, key)判斷對(duì)象obejct是否包含key屬性
平時(shí)你可能經(jīng)常這樣去判斷一個(gè)對(duì)象是否包含某個(gè)屬性
if (obj && obj.key) { // xxx }
但是這樣做有缺陷,比如某個(gè)屬性其對(duì)應(yīng)的值為0,null,false,""空字符串呢?這樣明明obj有以下對(duì)應(yīng)的屬性,卻因?yàn)閷傩灾禐?strong>假而通過不了驗(yàn)證
let obj = { name: "", sex: 0, handsomeBoy: false, timer: null }
所以我們可以采用下劃線中的這種方式
源碼
var hasOwnProperty = ObjProto.hasOwnProperty; _.has = function(obj, key) { return obj != null && hasOwnProperty.call(obj, key); };_.keys(object)
獲取object對(duì)象所有的屬性名稱。
使用示例
let obj = { name: "qianlongo", sex: "boy" } let keys = _.keys(obj) // ["name", "sex"]
源碼
_.keys = function(obj) { // 如果obj不是object類型直接返回空數(shù)組 if (!_.isObject(obj)) return []; // 如果瀏覽器支持原生的keys方法,則使用原生的keys if (nativeKeys) return nativeKeys(obj); var keys = []; // 注意這里1、for in會(huì)遍歷原型上的鍵,所以用_.has來確保讀取的只是對(duì)象本身的屬性 for (var key in obj) if (_.has(obj, key)) keys.push(key); // Ahem, IE < 9. // 這里主要處理ie9以下的瀏覽器的bug,會(huì)將對(duì)象上一些本該枚舉的屬性認(rèn)為不可枚舉,詳細(xì)可以看collectNonEnumProps分析 if (hasEnumBug) collectNonEnumProps(obj, keys); return keys; };collectNonEnumProps函數(shù)分析
該函數(shù)為下劃線中的內(nèi)部函數(shù)一枚,專門處理ie9以下的枚舉bug問題,for in到底有啥bug,終于可以說出來了。
簡(jiǎn)單地說就是如果對(duì)象將其原型上的類似toString的方法覆蓋了的話,那么我們認(rèn)為toString就是可枚舉的了,但是在ie9以下的瀏覽器中還是認(rèn)為是不可以枚舉的,又是萬惡的ie
源碼
// 判斷瀏覽器是否存在枚舉bug,如果有,在取反操作前會(huì)返回false var hasEnumBug = !{toString: null}.propertyIsEnumerable("toString"); // 所有需要處理的可能存在枚舉問題的屬性 var nonEnumerableProps = ["valueOf", "isPrototypeOf", "toString", "propertyIsEnumerable", "hasOwnProperty", "toLocaleString"]; // 處理ie9以下的一個(gè)枚舉bug function collectNonEnumProps(obj, keys) { var nonEnumIdx = nonEnumerableProps.length; var constructor = obj.constructor; // 讀取obj的原型 var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto; // 這里我有個(gè)疑問,對(duì)于constructor屬性為什么要多帶帶處理? // Constructor is a special case. var prop = "constructor"; if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop); while (nonEnumIdx--) { prop = nonEnumerableProps[nonEnumIdx]; // nonEnumerableProps中的屬性出現(xiàn)在obj中,并且和原型中的同名方法不等,再者keys中不存在該屬性,就添加進(jìn)去 if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) { keys.push(prop); } } }
代碼看起來并不復(fù)雜,但是有一個(gè)小疑問,對(duì)于constructor屬性為什么要多帶帶處理呢?各個(gè)看官,如果知曉,請(qǐng)教我啊
_.allKeys(object)獲取object中所有的屬性,包括原型上的。
舉個(gè)簡(jiǎn)單的例子說明
let Person = function (name, sex) { this.name = name this.sex = sex } Person.prototype = { constructor: Person, showName () { console.log(this.name) } } let p = new Person("qianlongo", "boy") _.keys(p) // ["name", "sex"] 只包括自身的屬性 _.allKeys(p) // ["name", "sex", "constructor", "showName"] 還包括原型上的屬性
接下來看下源碼是怎么干的
源碼
// 獲取對(duì)象obj的所有的鍵 // 與keys不同,這里包括繼承來的key // Retrieve all the property names of an object. _.allKeys = function(obj) { if (!_.isObject(obj)) return []; var keys = []; // 直接讀遍歷取到的key,包括原型上的 for (var key in obj) keys.push(key); // Ahem, IE < 9. if (hasEnumBug) collectNonEnumProps(obj, keys); // 同樣處理一下有枚舉問題的瀏覽器 return keys; };
可以看到和_.keys的唯一的不同就在于遍歷obj的時(shí)候有沒有用hasOwnProperty去判斷
_.values()返回object對(duì)象所有的屬性值。
使用案例
let obj = { name: "qianlongo", sex: "boy" } _.values(obj) // ["qianlongo", "boy"]
源碼
// Retrieve the values of an object"s properties. _.values = function(obj) { // 用到了前面已經(jīng)寫好的keys函數(shù),所以values認(rèn)為獲取的屬性值,不包括原型 var keys = _.keys(obj); var length = keys.length; var values = Array(length); for (var i = 0; i < length; i++) { values[i] = obj[keys[i]]; } return values; };_.invert(object)
返回一個(gè)object副本,使其鍵(keys)和值(values)對(duì)換。
使用案例
let obj = { name: "qianlongo", secName: "qianlongo", age: 100 } _.invert(obj) // {100: "age", qianlongo: "secName"}
注意喲,如果對(duì)象中有些屬性值是相等的,那么翻轉(zhuǎn)過來的對(duì)象其key取最后一個(gè)
源碼
_.invert = function(obj) { var result = {}; // 所以也只是取對(duì)象本身的屬性 var keys = _.keys(obj); for (var i = 0, length = keys.length; i < length; i++) { // 值為key,key為值,如果有值相等,后面的覆蓋前面的 result[obj[keys[i]]] = keys[i]; } return result; };_.functions(object)
返回一個(gè)對(duì)象里所有的方法名, 而且是已經(jīng)排序的(注意這里包括原型上的屬性)
源碼
_.functions = _.methods = function(obj) { var names = []; for (var key in obj) { // 是函數(shù),就裝載進(jìn)去 if (_.isFunction(obj[key])) names.push(key); } return names.sort(); // 最后返回經(jīng)過排序的數(shù)組 };結(jié)尾
夜深人靜,悄悄地說一個(gè)bug這個(gè)鬼故事講完了,各位good night。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/82939.html
摘要:從最大的同性社交平臺(tái)獲取數(shù)據(jù)好了,言歸正傳,回到題目。烏云密布的爬蟲百度網(wǎng)盤這件事,是我不想看到的,這類安全問題的一個(gè)共同特點(diǎn)用戶自身確實(shí)存在問題。 本文作者:夏之冰雪,i春秋簽約作家 《我在百度網(wǎng)盤上看到上萬條車主個(gè)人信息,企業(yè)、政府高官信息、各種數(shù)據(jù)庫和無窮無盡的盜版》,一時(shí)間,這篇文章就火了,火爆程度另百度猝不及防。 其實(shí)呢,這事真不能全怪百度,畢竟用戶分享出去了。之所以引起這么...
摘要:蘋果公司也參與進(jìn)來蘋果將在新版上取消耳機(jī)插孔,取而代之的可能是一對(duì)無線。蘋果在上線之前就建立了全世界第一個(gè)數(shù)據(jù)中心,耗資億美元,用來進(jìn)行的計(jì)算。它是一個(gè)蜷縮在你的耳膜附近的或者。外加你還得忍受耳廓的疼痛。整體滿意度將只能徘徊在或。 多希望我可以觸碰你。 西奧多躺在床上,靜靜地說。他的生活中充滿了沉默與拒絕,但這一次,塞曼莎溫柔地問:你想怎樣觸碰我呢? showImg(https://s...
摘要:類似地,輸入中的大規(guī)模特征將主導(dǎo)網(wǎng)絡(luò)并導(dǎo)致下游發(fā)生更大的變化。因此,使用神經(jīng)網(wǎng)絡(luò)庫的自動(dòng)規(guī)范化往往是不夠的,這些神經(jīng)網(wǎng)絡(luò)庫會(huì)在每個(gè)特征的基礎(chǔ)上盲目地減去平均值并除以方差。 如果你的神經(jīng)網(wǎng)絡(luò)不工作,該怎么辦?作者在這里列出了建神經(jīng)網(wǎng)絡(luò)時(shí)所有可能做錯(cuò)的事情,以及他自己的解決經(jīng)驗(yàn)。忘記規(guī)范化數(shù)據(jù)忘記檢查結(jié)果忘記預(yù)處理數(shù)據(jù)忘記使用正則化使用的batch太大使用了不正確的學(xué)習(xí)率在最后層使用了錯(cuò)誤的激活...
摘要:前端開發(fā)在年依然持續(xù)火熱,本文將對(duì)熱點(diǎn)事件做一個(gè)總結(jié)。版的和協(xié)議在前端領(lǐng)域,一直獨(dú)占鰲頭。年又發(fā)布了一個(gè)重大的版本更新。主要是配合使用了服務(wù)工作線程。而且還提供了供前端開發(fā)者接入。快速發(fā)布了和在悄悄地跳過之后,在月號(hào)正式發(fā)布。 譯者按: 老技術(shù)日趨成熟,新技術(shù)層出不窮。 原文: A recap of front-end development in 2017 譯者: Fundebu...
閱讀 1683·2021-10-13 09:39
閱讀 3154·2021-10-12 10:11
閱讀 549·2021-09-28 09:36
閱讀 2633·2019-08-30 15:55
閱讀 1384·2019-08-30 13:04
閱讀 621·2019-08-29 17:08
閱讀 1900·2019-08-29 14:14
閱讀 3399·2019-08-28 18:23