for...injavascript
枚舉一個對象的全部可枚舉屬性java
檢測DOM/BOM屬性程序員
if ("onclick" in elem) { // 元素支持onclick } if ("onerror" in window) { // window支持onerror }
檢測js對象的原型屬性(結合hasOwnProperty()函數)正則表達式
if ("attr" in obj && !obj.hasOwnProperty("attr")) { // obj有原型屬性attr }
注意:原型屬性會被同名的實例屬性屏蔽掉,因此沒法檢測這樣的:json
function Super(){ this.attr = 1; } function Sub(){ this.attr = 2; } Sub.prototype = new Super(); var obj = new Sub(); alert(("attr" in obj && !obj.hasOwnProperty("attr"))); /// false
||能夠用來填充默認值,例如:數組
var obj = {}; obj.name = ""; // 注意js有一堆假值:+0、-0、0、NaN、null、undefined、""、''、false var name = obj.name || "ayqy"; alert(name);
P.S.我的認爲這個東西並很差用,除非能肯定原屬性值不多是任何形式的假值,不然默認值會覆蓋原值瀏覽器
&&能夠用來避免從undefined值讀取屬性引發TypeError,例如:緩存
var obj = {}; //var attr = obj.obj.attr; // 報錯,TypeError,不能從undefined值讀取屬性 var attr = obj.obj && obj.obj.attr; // 只有obj.obj存在且不是假值時纔會取其attr
P.S.我的認爲和&&差很少,不如直接用if檢測,不只能展示更清晰的邏輯流,還便於添加更多的檢測條件閉包
P.S.這兩種用法是在書的第一部分介紹的,可能更偏向於展現語言特性,並非推薦用法app
我的建議不要這樣用,固然,看到這樣的代碼要能明白其做用與漏洞
用「命名空間」,即空對象,實質是把建立的全局變量減小到1個,好比YUI的Y對象,JQuery的JQuery和$對象。。例如:
var app = {}; //命名空間:app app.Consts = { // 子命名空間:常量 URL : { // 子子命名空間:URL } } app.Modules = { // 子命名空間:模塊 } app.Data = { // 子命名空間:數據 }
用IIFE(匿名函數當即執行),實質徹底不建立全局變量了,0污染
(function(){ // Module1 })(); (function(){ // Module2 })();
但缺點很明顯,沒法實現對象緩存和共享,出了閉包就沒了
因此通常作法是結合命名空間和IIFE,總體用命名空間組織起來,在模塊內部合適的地方用IIFE
方法調用模式:obj.fun();或者obj[」fun」]();
函數調用模式:fun();此時this指向global對象
構造器調用模式:new Fun();新對象的prototype指向Fun的prototype,this指向這個新對象
apply調用模式:fun.apply(this, arrArgs);
arguments對象不是數組對象,也不支持全部數組方法,只是一個有點特殊的普通對象,特殊在其length屬性(動態更新)
能夠作以下測試:
function fun(){ var x = Object.prototype.toString.call(arguments); alert(x); var arr = []; alert(Object.prototype.toString.call(arr)); }; fun(); // IE8:[object Object]和[object Array],Chrome:[object Arguments]和[object Array] // 不同沒關係,反正都不是Array
一個小技巧:能夠用slice方法把arguments對象轉換爲數組,例如:
function fun(){ //arguments.sort(); // 報錯,不支持sort()函數 var arr = Array.prototype.slice.call(arguments); // 轉換 arr.sort(); //不報錯,說明轉換成功 alert(arr); }; fun(3, 2); // 2, 3
注意:只有slice有這種奇效,concat就沒有,雖然無參的slice和無參的concat用於數組的效果同樣(都是複製一份)
讓沒有返回值的函數返回this,因此支持鏈式調用,好比JQuery中的:
$("#adBlock").removeClass("active").hide();
若是不用new操做符,直接調用構造函數,好比Fun();此時this指向global屬性,即瀏覽器環境下的window對象,可能會污染全局做用域
例如:
function fun(height, width, margin, padding){ // 參數太長,順序記不住 // do something } /* * @param {number} arg.height 高度 * @param {number} arg.width 寬度 * @param {number} arg.margin 留白 * @param {number} arg.padding 補白 */ function fun(arg){ // 不用記參數順序了 // do something }
好處以下:
調用時不用再關心參數順序了,接口變得更加易用
能夠直接傳個JSON對象進去
缺點:要寫一堆註釋說明具體須要哪些參數,由於經過一個arg徹底看不出來
防僞對象的屬性能夠被替換或者刪除,但該對象的完整性不會受到損害
也被稱爲持久性的對象,一個持久性對象就是一個簡單功能函數的集合
P.S.防僞對象的概念出如今書的[函數化]部分,目前還不能徹底理解函數化想要表達的意思,把本身養肥了再看
本質是鍵值對,也就是對象,因此並無訪問速度上的優點
區別是Array.prototype提供了不少數組操做函數,此外還有特殊的length屬性
注意: 1. 數組實際佔用的內存空間
var arr = [1]; arr[99] = 100;
此時arr指向的值並無佔用100個元素的空間,由於上面的結果至關於:
var arr = { "0" : 1, "99" : 100 };
length屬性是可寫的
能夠用arr.length = n;刪掉下標值 >= n的全部元素
常見的length屬性用法
省計數器的方式:arr[arr.length] = value;
或者更簡單的:arr.push(value);
數組類型檢測
由於typeof arr返回」object」,並且instanceof操做符在跨frame時失效,因此檢測數組類型不太容易
書上給了一種超麻煩的方式:
function isArray(value){ return value && typeof value === "object" && typeof value.length === "number" && typeof value.splice === "function" && !(value.propertyIsEnumerable("length")); }
其實有在做者寫書的時候還沒出現的簡單方法:
能夠用Object.prototype.toString.call(value) === ‘[object Array/Function…]’來作類型檢查,也能夠用來區分原生對象和自定義對象,例如:
[object JSON] // 原生JSON對象 [object Object] // 自定義JSON對象
關於類型檢測的更多信息請查看黯羽輕揚:JS學習筆記11_高級技巧
js的正則表達式功能並不完整,但基本夠用,好比只支持3種模式:g/i/m ~ 全局模式/忽略大小寫模式/多行模式
並且支持的特殊元字符(\d、\s之類的)也比較少,但好在支持非捕獲型分組和正則環視,仍是很不錯的
因此在js中使用正則表達式須要作更多的測試,更多關於正則表達式的信息請查看黯羽輕揚:正則表達式學習筆記
還有一個不經常使用的點:RegExp對象的屬性
global:是否開了g模式
ignoreCase:返回是否開了i模式
lastIndex:返回下一次exec匹配的起始位置
multiline:返回是否開了多行模式
source:返回正則表達式串
須要特別注意:用字面量方式建立的RegExp對象可能引用同一個實例,例如:
var regex1 = /abc/g; var regex2 = /abc/g; var str = "aaa\r\nabc\r\naaa"; alert([regex1.lastIndex, regex2.lastIndex]); // 0, 0 regex1.test(str); alert([regex1.lastIndex, regex2.lastIndex]); // 8, 0 alert(regex1 === regex2); // false
老版本瀏覽器最後兩行會輸出8, 8和true,聽說正則字面量共享實例是ES3的規定,本機測試發現IE8不存在問題,但理論上IE6就存在這個問題(出IE6的時候,ES5還沒出)
P.S.此處只介紹須要特別注意的點,完整的各類操做函數請查看黯羽輕揚:JS學習筆記1_基礎與常識
arr1.push(arr2);會把arr2做爲單個元素插入,例如:
var arr1 = [1]; var arr2 = [2, 3]; arr1.push(arr2); alert(arr1[2]); // undefined alert(arr1[1][1]); // 3
arr.shift()比arr.pop()慢不少,由於刪掉首元須要更新全部元素的索引,而刪掉尾元不須要
arr.unshift(),在數組頭部插入元素,IE6返回undefined,原本應該返回新數組的長度
obj.hasOwnProperty(「hasOwnProperty」)返回false
regex.exec(str)函數功能最強大,固然,執行速度也最慢
regex.test(str)最簡單,最快,注意:不要開g模式,由於純屬浪費(test只返回匹配/不匹配,一次掃描就夠了)
str.replace(regex, fun);是很好用的一個函數,能夠用regex匹配目標部分,還能夠用fun進一步處理匹配的部分
特別注意:若是regex沒有開g模式,那麼只替換第一個匹配部分,並非全串替換
str.replace(regex, replacement);的replacement部分中的$有特殊含義:
$$:表示$,若是替換部分中有$須要用$來轉義
$&:表示整個匹配的文本
$n:例如$1, $2, $3...表示捕獲到的文本
$`:表示位於匹配部分以前的文本
$’:表示位於匹配部分以後的文本
更多詳細示例請查看W3School:JavaScript replace() 方法
str.split(regex);存在一個特例,須要特別注意:
var str = "a & b & c"; // &是分隔符 var arr = str.split(/\s*(&)\s*/); // 含捕獲 var arr2 = str.split(/\s*&\s*/); // 不含捕獲 var arr3 = str.split(/\s*(?:&)\s*/); // 含非捕獲 alert(arr); // [a, &, b, &, c] alert(arr2); // [a, b, c] alert(arr3); // [a, b, c]
若是不當心用了含捕獲型分組的正則表達式,結果可能與預期的不一樣
P.S.這裏不打算原樣照搬書上的內容,只給出筆者贊成的最糟糕的地方
自動插入分號。確實很差,好比典型的ASI錯誤:
function fun(){ return { attr : 1; } } alert(fun().attr); // TypeError: Cannot read property 'attr' of undefined
typeof。很差用,這是設計上的失誤,歷史緣由
paseInt()與八進制。一個很隱蔽的錯誤:
var year = "2015"; var month = "08"; var day = "09"; alert(parseInt(year)); // IE8:2015 Chrome:2015 alert(parseInt(month)); // IE8:0 Chrome:8 alert(parseInt(day)); // IE8:0 Chrome:9
由於0開頭的數值被認爲是八進制,省略第二個參數的parseInt()會把08/09看成八進制來解析,因此結果是0
處理時間日期太容易引發解析錯誤了,因此最好不要省略parseInt()的第二個參數
P.S.Chrome中正常了,多是某個ES版本對parseInt()作了修改,Chrome作了實現。因此糟粕並非永遠存在的,固然,是否是糟粕也要看程序員怎麼用。。
Unicode。js對Unicode的支持很差,雖然也是歷史緣由,但不支持就是不支持
P.S.出js的時候Unicode本身也沒想到能有100萬個字符,js的字符是16位的,能對應前65536個Unicode字符,而Unicode爲了擴容,就把剩下的每一個字符都用一對字符來表示,但js會把這樣的字符看成兩個字符,因此。。
P.S.同上,只列筆者贊成的
continue性能低。能夠if消除
位運算性能低。(其它的書都沒有提到這一點)由於要折騰:double -> int -> 作位運算 -> double
new到底應該用仍是不用。用的話可能污染做用域
void運算符。單目運算,返回undefined,因此能夠用來實現IIFE,例如:
void function(){ // do something }();
做者建議不要用void,由於沒什麼人知道它是作什麼的
整數的首位不能是0,道格拉斯(發明JSON的大爺)本人說的,數值能夠是整數、實數或者科學計數,但首位不能是0,爲了不八進制歧義
測試代碼以下:
var json = '{"number" : 9}'; var obj = JSON.parse(json); alert(obj.number); // 9 var json = '{"number" : 09}'; // 注意前導0 var obj = JSON.parse(json); alert(obj.number); // 報錯 // Chrome:SyntaxError: Unexpected number // IE8:語法錯誤 // FF:SyntaxError: JSON.parse: expected ',' or '}' after property value in object