偶然看到JavaScript Tips。這個項目是天天分享一個JavaScript Tip,受到這個項目的啓發,我也打算天天更新一個小Tip,我會參考原文,若是以爲寫的很不錯,我也會僅僅充當翻譯工,可是每個Tip都會加入我本身的思考,思考若是是我來寫這個Tip的話,我會怎麼寫。git
經驗不足,能力有限,還望輕拍。程序員
2016-01-07 至 2016-01-16:github
var arr = ["abc", "undefined","abc","xyz"]; var arr2 = arr.filter(function(item, index){ return arr.indexOf(item) === index; }); console.log(arr2); // ["abc", "undefined", "abc", "xyz"]
上面這種去重方式使用到了indexOf
這個函數,返回第一次查找到該元素的索引,用來與該元素在數組中的位置進行比較數組
關鍵詞:數組去重、indexOf閉包
(function(name){ console.log(name); })("sunyuhui");
上面這種寫法就是當即執行函數(更準確的叫法是當即執行匿名函數)。其中包含了兩個知識點:框架
()
能夠執行函數。使用當即執行函數的好處是能夠避免函數裏定義的變量污染全局變量。由於JS裏沒有局部變量這個概念(ES2015開始有局部變量的概念)。因此在須要使用到一些比較短的代碼同時又不但願污染全局變量的時候,就可使用當即執行函數。函數
關鍵詞:當即執行函數性能
===
和==
的區別在於===
不會進行類型轉換,而==
會進行類型轉換。測試
console.log('1' == 1); // true console.log('1' === 1); // false
在使用時我建議使用===
,至少我在工做中是這樣的,不過也有可能會有須要使用==
的場景,這裏須要注意一下。優化
關鍵詞:===、==
一般的作法是使用Number()
.
var a = '1'; var b = Number(a); console.log(b + ' ' +typeof b); // 1 number
這裏介紹一種更加簡單的方法。
var a = '1'; var b = +a; console.log(b + ' '+ typeof b); // 1 number
使用運算符+
能夠快速轉換爲Number類型的數字。
關鍵詞:String、Number
清空數組一般的作法是直接使用arr = []
的方式。不過這裏想介紹另外一種方式:使用屬性length
。
var a = [1,2,3]; a.length = 0; console.log(a); // []
其實我在2016-01-07:往數組中插入一個元素就提到過,JS中直接操做數組的length
屬性,會影響到數組具體的內容。這裏咱們再來看看這兩種清空數組的方式 有什麼不一樣。
var a = [1,2,3]; var b = a; a = []; console.log(a); // [] console.log(b); // [1,2,3] var c = [1,2,3,4]; var d = c; c.length = 0; console.log(c); // [] console.log(d); // []
使用將屬性length設置爲零的方式會把從原數組複製的數組也清空,而使用arr = []
的方式則不會。
關鍵詞:length、清空數組
實現鏈式調用一般是在jQuery
或者其餘一些框架裏,那咱們如何在原生的JavaScript
裏實現鏈式調用呢?
function Person(name){ this.name = name; this.setName = function(name){ this.name = name; return this; } this.sayName = function(){ console.log(this.name); return this; } } var person = new Person("sunyuhui"); person.sayName().setName("sunyuhui2").sayName();// "sunyuhui" "sunyuhui2"
關鍵詞:鏈式調用、this
拼接字符串一般的作法是使用加號+
,如c = '1' + '2';
使用加號+
的前提是要確保參數都是字符串,萬一有參數不是字符串,就得不到指望的結果:
var a = 1; var b = 2; var c = '3'; var d = a+b+c; console.log(d); //"33"
這種時候可使用concat函數
.
var a = 1; var b = 2; var c = '3'; var d = ''.concat(a,b,c); console.log(d); //"123"
注意:Array
也有concat
函數,要注意區分。
關鍵詞: concat
var a = 1; function get(){ console.log(a); } get(); //1 function get2(){ var a = 2; console.log(a); } get2(); //2
上面代碼的輸出結果你們確定明白,可是下面的代碼呢?
var a = 1; function get(){ console.log(a); } function get3(){ var a = 2; get(); } get3(); //1
輸入結果爲1的緣由是因爲函數嵌套時,做用域是在定義時決定的,而不是調用時決定的。函數get
是在全局做用域下定義的,而運行是在函數get3
的做用域下定義的,獲取變量a
的值是在全局做用域下獲取的,而不是在函數get
的做用域裏獲取的。
關鍵詞: 嵌套、做用域
function Person(name,age){ this.name = name; this.age = age; } var person1 = new Person("sunyuhui", "22");
咱們以上面的代碼爲例說明new
這個關鍵字作了哪些事。
Person
的做用域賦給person1,這樣對象person1就能夠訪問構造函數Person
裏的代碼。this就指向對象person1。name
和age
分別被賦值爲sunyuhui、22。關鍵詞: new
典型的遞歸函數是求一個數的階乘:
function getTotal(num){ if (num<2){ return 1; } else { return num * getTotal(num-1); } } console.log(getTotal(3)); // 6
這裏說到的優化主要指的是函數在調用自身的時候能夠不用指定函數名,防止函數被從新賦值致使調用出錯。
function getTotal(num){ if (num<2){ return 1; } else { return num * arguments.callee(num-1); } } console.log(getTotal(3)); // 6
其中arguments
指向函數的參數,arguments.callee
指向正在執行的函數,
關鍵詞: 遞歸、arguments.callee
在【2016-01-15:實現JS中的繼承】中我提到Man.prototype=
的形式會破壞構造函數的原型鏈。那在這種狀況下咱們有什麼辦法不破壞嗎?
固然有
function Person(name){ this.name = name; } Person.prototype = { //constructor: Person, getName: function(){ return this.name; } } var man = new Person("sunyuhui"); console.log(man.constructor === Person); // false
每個構造函數的原型都有一個construtor
屬性,指向構造函數自己,上面這種設置原型的方式至關於重寫了原型,卻沒有爲原型的constructor
屬性賦值,就破壞了Person
的原型鏈。將註釋的部分取消註釋就ok了。
關鍵詞:constructor、原型鏈
function Person (name,age,pro){ this.name = name; this.age = age; this.pro = pro; } Person.prototype.getPro = function(){ return this.pro; } function Man (name,age,pro){ this.name = name; this.age = age; } Man.prototype = new Person("sunyuhui","22","fe"); var man1 = new Man("sunyuhui2","23"); console.log(man1.name); // sunyuhui2 console.log(man1.age); // 23 console.log(man1.getPro()); // fe
將構造函數Person
的實例賦予構造函數Man
的原型,這樣就實現了Man
對Person
的繼承,從中能夠看到man1
的屬性name和屬性age覆蓋了繼承的屬性,而屬性fe被來自於繼承。
注意:Man.prototype = ***
的這種形式會修改原型鏈,使Man
的實例對象的constructor
屬性不爲Man
.這個點之後單獨說。
關鍵詞: 繼承
說到console
咱們一般使用console.log()
來輸出變量或者打印日誌,這裏介紹一個不太經常使用的console.time()
和console.timeEnd()
。
console.time("time test"); var arr = new Array(1000); for(var i=0;i<arr.length;i++){ arr[i] = {}; } console.timeEnd("time test"); // time test: 4.037ms
須要注意console.time()
和console.timeEnd()
的參數須要保持相同。
咱們一般會使用typeof
、instanceof
、isArray
來檢測數據的類型,這裏我介紹一種萬能型的:調用Object
的toString
方法。
function a(){console.log("yes");} console.log(Object.prototype.toString.call(a)); // [object Function] var b = 123; console.log(Object.prototype.toString.call(b)); // [object Number] var c = "sunhui"; console.log(Object.prototype.toString.call(c)); // [object String] var d = true; console.log(Object.prototype.toString.call(d)); // [object Boolean] var e = [1,2,3]; console.log(Object.prototype.toString.call(e)); // [object Array] var f; console.log(Object.prototype.toString.call(f)); // [object Undefined] var g = null; console.log(Object.prototype.toString.call(g)); // [object Null] var h = {"name":"sunyuhui"}; console.log(Object.prototype.toString.call(h)); // [object Object]
使用call
調用Object
的toString
方法,將會返回一個遵循[object NativeConstructorName]格式的字符串。其中NativeConstructorName指的就是變量的構造函數名。
在JavaScript裏 變量聲明 是讓系統知道這個變量存在,而變量定義是給這個變量賦值。變量聲明會被提早到頂部,而 變量定義 不會。
console.log(a); // "ReferenceError: a is not defined"
上面的代碼報變量沒有定義的錯誤。
console.log(a); // undefined var a;
上面的代碼提示undefined
,這說明咱們對a
的聲明被提早了。再來看:
console.log(a); // undefined var a = 3;
咱們在這裏對a
同時作了聲明和定義兩個操做,從結果來看,只有聲明被提早了,而定義卻沒有。
接下來看函數:
getName(); // "sunyuhui" function getName(){ console.log("sunyuhui"); }
結果說明咱們對函數的聲明被提早了。
getAge(); // "TypeError: getAge is not a function" var getAge = function(){ console.log(22); }
事實上,第一種方式叫函數聲明,這種方式能被提早到頂部,第二種方式叫函數表達式,不會被提早。
關鍵詞:聲明、 提早
咱們常常須要用到對象中的某個屬性,爲了保證程序的健壯性,在使用以前咱們須要判斷這個屬性是否存在,能夠用if
來判斷
if(obj[property] !== undefined){ // do something }
更方便的方式是使用這兩個方法:hasOwnProperty
和in
。
function Person (name) { this.name = name; } Person.prototype.age = '22'; var person = new Person("sunhui"); console.log(person.hasOwnProperty('name')); // true console.log("name" in person); // true console.log(person.hasOwnProperty('age')); // false console.log("age" in person); // true
hasOwnProperty
和in
均可以用來判斷某個屬性是否存在於對象中,區別就是hasOwnProperty
不能搜索到從原型鏈繼承的屬性,而in
能夠。
關鍵詞:hasOwnProperty、in、原型鏈
function compare(prop){ return function(obj1,obj2){ var prop1 = obj1[prop]; var prop2 = obj2[prop]; if(prop1<prop2){ return -1; } else if(prop1>prop2){ return 1; } else { return 0; } } } var arr = [ {"name":"bsunhui","age":"23"}, {"name":"csunhui","age":"22"} ]; arr.sort(compare("age")); console.log(arr); //[{"name":"csunhui","age":"22"},{"name":"bsunhui","age":"23"}] arr.sort(compare("name")); console.log(arr); //[{"name":"bsunhui","age":"23"},{"name":"csunhui","age":"22"}]
compare
函數返回了一個閉包,這樣就能夠根據傳入的屬性進行排序。當比較字符串時,最好用 localeCompare()
方法來比較字符串(字符會按字母表的先後位置關係比較),不然比較的字符的是ascii碼。注意數字字符串會被隱式轉換爲數值類型。
關鍵詞:閉包、對象數組按屬性排序、字符串比較
舉例:
var a = "sun"; var b = ["sun","hui"];
如今須要將a
轉換成大寫,將b
中的每一個元素都轉換成大寫,一般的作法是:
console.log(a.toUpperCase()); // "SUN" for(var i=0;i<b.length;i++){ console.log(b[i].toUpperCase()); // "SUN" "HUI" }
這種作法至關於寫了兩套代碼,看看下面的方式。
function toUpper(words){ var elements = [].concat(words); for(var i=0;i<elements.length;i++){ console.log(elements[i].toUpperCase()); } } toUpper("sun"); // "SUN" toUpper(["sun","hui"]); // "SUN" "HUI"
關鍵點是:concat
的用法,str.concat(arg)
的參數arg
若是是一個數組會把數組裏的(不是嵌套數組的)每一項都拿出來做爲新數組的元素。
關鍵詞:concat
undefined
表示變量沒有被聲明或者聲明瞭可是沒有被賦值。null
表示這個變量不是一個值。undefined
。null
,只有程序員本身才能把一個值設置爲null
。undefined
不可用,而null
是可用的。type of undefined
的值爲undefined
。type of null
的值爲object
。null
和undefined
的布爾值都爲false。null == undefined
的值爲true。null === undefined
的值爲false。關鍵詞:null、undefined
通常而言,對數組元素的操做咱們會用到這些函數:push
,pop
,unshift
,shift
,splice
。
var a = [1, 2, 3]; a.push(4); console.log(a); //[1,2,3,4] a.pop(); console.log(a); //[1,2,3] a.unshift(5); console.log(a); //[5,1,2,3] a.shift(); console.log(a); //[1,2,3] a.splice(0,2,6); console.log(a) //[6,3]
我在這裏想介紹的是利用length
屬性來進行數組元素的增減。
var a = [1, 2, 3]; a.length = 1; console.log(a); //[1] a.length = 5; console.log(a); //[1,undefined,undefined, undefined, undefined] a = [1,2,3]; a.splice(a.length/2,0,4,5,6); //在數組的中間插入元素 console.log(a); //[1,4,5,6,2,3]
值得注意的是以上全部方法都改變了原數組。咱們再來一個:
var a = [1,2,3]; var b = [4,5].concat(a); console.log(a); //[1,2,3] console.log(b); //[4,5,1,2,3]
使用concat
來增長數組元素就會返回一個新數組,不會改變原數組。
關鍵詞:數組、length