javascript專精--常見高階技巧
原文:https://blog.csdn.net/mapbar_front/article/details/78484963
1、型別檢測
JavaScript中提供兩種型別檢測語法——typeof、instanceof。但是這兩種都有一定的缺陷。
typeof僅僅適用於基本型別的檢測判斷,但無法對Array型別和Object型別進行區分;
instanceof只能對資料進行基本的true或者false的判定,而無法像typeof的方式進行快速判定。
一般成熟的做法是使用Object.prototype.toString.call(value)的方式進行型別判斷。
(1)、typeof的方式:
var num = 1;
console.log(typeof num);//number
var obj = null;
console.log(typeof obj);//object
var arr = [];
console.log(typeof arr);//object
var obj1 = {};
console.log(typeof obj1);//object
(2)、instanceof的方式
var a = 1;
console.log(a instanceof Array);//false
console.log(a instanceof Number);//true
//優勢是能夠確定是屬於陣列Array還是屬於物件Object
var b = [];
console.log(b instanceof Array);//true
console.log(b instanceof Object);//false
(3)、Object.prototype.toString.call(value)的方式:
這種方式的原理就是:建構函式的原型的toString方法,會返回一個類似”[object type]”的字串。
function getType(value) {
if(arguments.length !== 1){
throw new TypeError('引數必須僅僅一個!');
}
if(Object.prototype.toString.call(value) == '[object Object]'){
return 'object';
} else if(Object.prototype.toString.call(value) == '[object Array]'){
return 'array';
} else if(Object.prototype.toString.call(value) == '[object Number]'){
return 'number';
} else if(Object.prototype.toString.call(value) == '[object String]'){
return 'string';
} else if(Object.prototype.toString.call(value) == '[object Function]'){
return 'function';
} else if(Object.prototype.toString.call(value) == '[object Boolean]'){
return 'boolean';
} else if(Object.prototype.toString.call(value) == '[object Null]'){
return 'null';
} else if(Object.prototype.toString.call(value) == '[object Undefined]'){
return 'undefined';
} else {
throw new Error('這是一個未知的型別!');
}
}
var a = undefined;
console.log(Object.prototype.toString.call(a));//直接使用Object.prototype.toString.call()
2、建構函式作用域安全機制
建構函式,是面向物件的核心,可是,在使用建構函式的過程中,會有什麼樣的坑呢?
function Cat(name){
this.name = name;
}
一般而言,new Cat(‘name’)是它的使用方式!!
但是,如果我執行 Cat(“name”) 呢?
導致的結果就是給window物件添加了一個name屬性。
(1)、如何解決???????
function Cat(name){
if(this instanceof Cat){
this.name = name;
} else {
return new Cat(name);
}
}
上面的建構函式,就被稱為作用域安全的建構函式。(本質是判斷this的作用域,或者是判斷this的指向問題。)
3、惰性函式載入
惰性函式載入,在我看來,可以認為是一種函式的再次定義。
function add(a,b){
return function(){
return a + b;
}
}
這樣的方式有什麼用呢?其實個人覺得,它最大的用處在於瀏覽器相容。一個非常經典的示例就是ajax的封裝!
function ajax(){
if(XMLHttpRequest){
var xhr = new XMLHttpRequest();
//other code
} else {
//code
}
}
//下面是惰性載入的方式
function ajax(){
if(XMLHttpRequest){
return function(){
xhr = new XMLHttpRequest();
//code
}
} else {
return function() {}
}
}
相對而言,第二種函式的使用方式是很有優勢的,第一種函式的方式每次都要判斷瀏覽器是否支援XMLHttpRequest物件,而第二種方式,其實我們只需要判斷一次,這就是惰性函式載入的好處。
4、函式柯里化
函式的柯里化,其實就是用已有的函式,傳入適當的引數,得到一個新的函式的過程。
函式柯里化(function currying),官方概念是這樣的:用於建立已經設定好了一個或多個引數的函式。
一般使用閉包的方式進行建立柯里化函式。(下面是一個通用的柯里化函式的建立方式)。
function curry(fn){
var arg1 = Array.prototype.slice.call(arguments, 1);
return function(){
var arg2 = Array.prototype.slice.call(arguments);
return fn.apply(null,arg1.concat(arg2));
}
}
5、函式繫結
個人認為,函式繫結的問題,重點是this的指向問題。
函式繫結中,Function.prototype.bind()是一個重要的實現方式。
一般有下面這樣的使用:
var obj = document.getElementById('root');
obj.onclick = function(){
console.log(this);//這裡代表obj物件。
}
//也有這樣的方式
obj.onclick = function(){
console.log(this);//這裡代表window
}.bind(this);
其實bind方法和call、apply有點類似,都是改變了this的指向問題。
有人說,bind()方法的內部實現原理是什麼?我自己用下面的方式寫一個簡單的demo。
Function.prototype.bind = function(obj){
var self = this;
return function(){
self.apply(obj,arguments);
}
}
考慮到函式的柯里化,我們需要給bind方法進行傳遞引數。bind函式的封裝應該如下:
Function.prototype.bind = function(context){
var self = this;
var arg1 = Array.prototype.slice.call(arguments,1);
return function(){
var arg2= Array.prototype.slice.call(arguments);
self.apply(context,arg1.concat(arg2));
}
}
最後,考慮到原型鏈的繼承,如果想在建構函式中正確的使用bind,可能我們還需要這樣:
Function.prototype.bind = function(context){
var self = this;
var F = function(){};
var arg1 = Array.prototype.slice.call(arguments,1);
var bound = function(){
var arg2= Array.prototype.slice.call(arguments);
self.apply(context,arg1.concat(arg2));
}
F.prototype = self.prototype;
bound.prototype = new F();
return bound;
}
這樣可能就是最完美的bind方法的實現了。