JavaScript-Tips

偶然看到JavaScript Tips。這個項目是天天分享一個JavaScript Tip,受到這個項目的啓發,我也打算天天更新一個小Tip,我會參考原文,若是以爲寫的很不錯,我也會僅僅充當翻譯工,可是每個Tip都會加入我本身的思考,思考若是是我來寫這個Tip的話,我會怎麼寫。git

經驗不足,能力有限,還望輕拍。程序員

2016-01-07 至 2016-01-16:github

2016-01-26:數組去重

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閉包

2016-01-25: 當即執行函數

(function(name){
    console.log(name);
})("sunyuhui");

上面這種寫法就是當即執行函數(更準確的叫法是當即執行匿名函數)。其中包含了兩個知識點:框架

  1. 匿名函數的聲明。
  2. 函數聲明以後使用()能夠執行函數。

使用當即執行函數的好處是能夠避免函數裏定義的變量污染全局變量。由於JS裏沒有局部變量這個概念(ES2015開始有局部變量的概念)。因此在須要使用到一些比較短的代碼同時又不但願污染全局變量的時候,就可使用當即執行函數函數

關鍵詞:當即執行函數性能

2016-01-24: 使用===代替==

=====的區別在於===不會進行類型轉換,而==會進行類型轉換。測試

console.log('1' == 1);      // true
console.log('1' === 1);     // false

在使用時我建議使用===,至少我在工做中是這樣的,不過也有可能會有須要使用==的場景,這裏須要注意一下。優化

關鍵詞:=====

2016-01-23: 將String類型的數字快速轉換爲Number類型的數字

一般的作法是使用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類型的數字。

關鍵詞:StringNumber

2016-01-22: 清空一個數組

清空數組一般的作法是直接使用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清空數組

2016-01-21:鏈式調用對象實例的方法

實現鏈式調用一般是在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

2016-01-20: 使用concat拼接字符串

拼接字符串一般的作法是使用加號+,如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

2016-01-19: 函數嵌套時的做用域

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的做用域裏獲取的。

關鍵詞: 嵌套做用域

2016-01-18: new這個關鍵字到底作了什麼

function Person(name,age){
    this.name = name;
    this.age = age;
}
var person1 = new Person("sunyuhui", "22");

咱們以上面的代碼爲例說明new這個關鍵字作了哪些事。

  1. 建立一個新對象person1
  2. 將構造函數Person的做用域賦給person1,這樣對象person1就能夠訪問構造函數Person裏的代碼。this就指向對象person1
  3. 執行構造函數裏的代碼,對象personnameage分別被賦值爲sunyuhui22
  4. 返回新對象,這樣咱們就獲得了對象person

關鍵詞: new

2016-01-17: 遞歸函數的優化

典型的遞歸函數是求一個數的階乘:

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-16: 注意不要破壞原型鏈

在【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原型鏈

2016-01-15: 實現JS中的繼承

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的原型,這樣就實現了ManPerson的繼承,從中能夠看到man1的屬性name和屬性age覆蓋了繼承的屬性,而屬性fe被來自於繼承。

注意:Man.prototype = ***的這種形式會修改原型鏈,使Man的實例對象的constructor屬性不爲Man.這個點之後單獨說。

關鍵詞: 繼承

2016-01-14: 測試JS代碼塊性能

說到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()的參數須要保持相同。

2016-01-13:檢測數據類型

咱們一般會使用typeofinstanceofisArray來檢測數據的類型,這裏我介紹一種萬能型的:調用ObjecttoString方法。

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調用ObjecttoString方法,將會返回一個遵循[object NativeConstructorName]格式的字符串。其中NativeConstructorName指的就是變量的構造函數名。

2016-01-12: 變量和函數的提早聲明

在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);
}

事實上,第一種方式叫函數聲明,這種方式能被提早到頂部,第二種方式叫函數表達式,不會被提早。

關鍵詞:聲明、 提早

2016-01-11:判斷對象中是否存在某個屬性

咱們常常須要用到對象中的某個屬性,爲了保證程序的健壯性,在使用以前咱們須要判斷這個屬性是否存在,能夠用if來判斷

if(obj[property] !== undefined){
    // do something
}

更方便的方式是使用這兩個方法:hasOwnPropertyin

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

hasOwnPropertyin均可以用來判斷某個屬性是否存在於對象中,區別就是hasOwnProperty不能搜索到從原型鏈繼承的屬性,而in能夠。

關鍵詞:hasOwnPropertyin原型鏈

2016-01-10:對象數組根據某個屬性排序

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碼。注意數字字符串會被隱式轉換爲數值類型。

關鍵詞:閉包對象數組按屬性排序字符串比較

2016-01-09: 爲數組或者單個元素應用相同的函數

舉例:

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

2016-01-08:null和undefined的區別

  • undefined表示變量沒有被聲明或者聲明瞭可是沒有被賦值。
  • null表示這個變量不是一個值。
  • JavaScript裏沒有被賦值的變量默認爲undefined
  • JavaScript裏不會把一個值設置爲null,只有程序員本身才能把一個值設置爲null
  • 在JSON裏undefined不可用,而null是可用的。
  • type of undefined的值爲undefined
  • type of null的值爲object
  • nullundefined的布爾值都爲false。
  • null == undefined的值爲true
  • null === undefined的值爲false

關鍵詞:nullundefined

2016-01-07:往數組中插入一個元素

通常而言,對數組元素的操做咱們會用到這些函數:pushpopunshiftshiftsplice

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

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息