一、安全的類型檢測(cè)
①為什么引入安全的類型檢測(cè),難道javascript內(nèi)置的類型檢測(cè)不靠譜嗎?
1) typeof 只能檢測(cè)基本的數(shù)據(jù)類型,對(duì)于引用類型全部返回object,所以不靠譜吧
2) instanceof 是可以檢測(cè)出引用類型,但是它在存在多個(gè)全局作用域的情況下,問題多多。
例如:var isArray = value instanceof Array; 只有value是一個(gè)數(shù)組,且value還必須和Array構(gòu)造函數(shù)再同一個(gè)作用域下返回true。如果value是在另個(gè)frame中,那么返回的必定是false
②解決問題:大家知道,在任何值上調(diào)用Object 原生的toString()方法,都會(huì)返回一個(gè)[object NativeConstructorName]格式的字符串。每個(gè)類在內(nèi)部都有一個(gè)[[Class]]屬性,這個(gè)屬性中就指定了上述字符串中的構(gòu)造函數(shù)名。且由于原生數(shù)組的構(gòu)造函數(shù)名與全局作用域無關(guān),因此使用toString()就能保證返回一致的值。
function SafeTypeCheck() {
if (typeof this.isNumber != "function") {
// Number類型
SafeTypeCheck.prototype.isNumber = function (value) {
return Object.prototype.toString.call(value) == "[object Number]";
};
// String類型
SafeTypeCheck.prototype.isString = function (value) {
return Object.prototype.toString.call(value) == "[object String]";
};
// Boolean類型
SafeTypeCheck.prototype.isBoolean = function (value) {
return Object.prototype.toString.call(value) == "[object Boolean]";
};
// Array類型
SafeTypeCheck.prototype.isArray = function (value) {
return Object.prototype.toString.call(value) == "[object Array]";
};
// Function類型
SafeTypeCheck.prototype.isFunction = function (value) {
return Object.prototype.toString.call(value) == "[object Function]";
};
// RegExp類型
SafeTypeCheck.prototype.isRegExp = function (value) {
return Object.prototype.toString.call(value) == "[object RegExp]";
};
// Null類型
SafeTypeCheck.prototype.isNull = function (value) {
return Object.prototype.toString.call(value) == "[object Null]";
};
// Undefined類型
SafeTypeCheck.prototype.isUndefined = function (value) {
return Object.prototype.toString.call(value) == "[object Undefined]";
};
// Object類型, 包括JOSN類型
SafeTypeCheck.prototype.isObject = function (value) {
return window.JSON && Object.prototype.toString.call(value) == "[object Object]";
};
// Native JSON類型
// var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON) == "[object JSON]";
}
}
// 測(cè)試
var checkType = new SafeTypeCheck();
alert(checkType.isFunction(function () {})); // true
alert(checkType.isArray([])); // true
alert(checkType.isString(123)); // false
二、作用域安全的構(gòu)造函數(shù)
①引子:先看一個(gè)例子:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
}
var zhang = new Person("zhang", 34, "worker");
// alert(zhang.name); // zhang
// alert(window.name); // 為空
②正常情況下這是完全沒有問題的,但當(dāng)Person在實(shí)例化時(shí)被忘記了new了,那么可想而知,this指向了window
var li = Person("li", 20, "student");
alert(li.name); // 報(bào)錯(cuò)
alert(window.name); // li
③因此,我們?cè)趯ふ乙粋€(gè)良好的代碼來屏蔽這樣的問題
function Person(name, age, job){
// 當(dāng)使用了new實(shí)例化時(shí),this指向Person
if(this instanceof Person) {
this.name = name;
this.age = age;
this.job = job;
} else {
// 當(dāng)忘記了new對(duì)象時(shí),自動(dòng)會(huì)加上一個(gè)new關(guān)鍵字,這樣就避免了指向window的問題
return new Person(name, age, job);
}
}
var zhang = new Person("zhang", 34, "worker");
// alert(zhang.name); // zhang
// alert(window.name); // 為空
var li = Person("li", 20, "student");
alert(li.name); // li
alert(window.name); // 為空
④同樣,安全作用域的構(gòu)造函數(shù)也是有bug的,如果你想繼承但只是用竊取模式,不是原型鏈繼承模式時(shí),會(huì)因?yàn)楦割愖饔糜虮绘i住而失敗的現(xiàn)象
function Student(grade) {
Person.call(this, "wang", 29, "teacher");
this.grade = grade;
}
var stu = new Student(100);
// 由于父類的作用域被鎖住,所以無法調(diào)用
alert(stu.name); // undefined
alert(stu.grade); // 100
⑤所以,我們沒辦法。只能老老實(shí)實(shí)的用原型鏈搞定它。
function Student(grade) {
Person.call(this, "wang", 29, "teacher");
this.grade = grade;
}
Student.prototype = new Person();
var stu = new Student(100);
// 這樣就ok了
alert(stu.name); // wang
alert(stu.grade); // 100
三、惰性載入函數(shù)
①引子:為什么要有惰性載入函數(shù)?先看下面一個(gè)創(chuàng)建XHR對(duì)象的例子:
function createXHR(){
if (typeof XMLHttpRequest != "undefined"){ // 非ie
return new XMLHttpRequest();
} else if (typeof ActiveXObject != "undefined"){ // ie
if (typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
"MSXML2.XMLHttp"],
i,len;
for (i=0,len=versions.length; i < len; i++){
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (ex){
//跳過
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
} else {
throw new Error("No XHR object available.");
}
}
②問題所在:上述例子的功能是:針對(duì)不同的瀏覽器創(chuàng)建一個(gè)XHR對(duì)象,函數(shù)中有許多if語句,在代碼執(zhí)行期間我們需要判斷。因?yàn)槲覀兪褂玫臑g覽器不會(huì)發(fā)送變化,在這個(gè)函數(shù)第二次被調(diào)用的時(shí)候,我個(gè)人認(rèn)為該函數(shù)的if語句就應(yīng)該不會(huì)再次去判斷是什么樣的瀏覽器而去實(shí)例化不同的XHR對(duì)象。因此我們提出了惰性載入函數(shù)。
③應(yīng)用:現(xiàn)在將上述的函數(shù)優(yōu)化一下:
function createXHR(){
if (typeof XMLHttpRequest != "undefined"){ // 非ie
arguments.callee = function () {
return new XMLHttpRequest();
}
} else if (typeof ActiveXObject != "undefined"){ // ie
arguments.callee = function () {
if (typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
"MSXML2.XMLHttp"],
i,len;
for (i=0,len=versions.length; i < len; i++){
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (ex){
//跳過
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}
} else {
arguments.callee = function () {
throw new Error("No XHR object available.");
}
}
return arguments.callee();
}
alert(new createXHR() instanceof XMLHttpRequest); // true
④優(yōu)點(diǎn):這樣,我們?cè)谡{(diào)用時(shí),就不需要執(zhí)行了if語句,這又提高了效率。
四、函數(shù)綁定
①引子:先看下面一個(gè)例子:
var handler = {
message: "Event handled",
handleClick: function(event){
alert(this.message);
}
};
var message = "window";
var btn = document.getElementById("btn");
EventUtil.addEvent(btn, "click", handler.handleClick); // undefined,less ie8彈出window
②原因:為什么會(huì)彈出undefined?在于沒有保存handler.handleClick()的環(huán)境,所以this 對(duì)象最后是指向了DOM 按鈕而非handler(在IE8 中)。因此我們可以用閉包來保留handler.handleClick()的環(huán)境,起碼在這個(gè)類中中。this 指向window。)
var handler = {
message: "Event handled",
handleClick: function(event){
alert(this.message);
}
};
var message = "window";
var btn = document.getElementById("btn");
EventUtil.addEvent(btn, "click", function(event){
handler.handleClick(event); // Event handled
});
③解決方案:雖然可以用閉包解決此類問題,但是使用閉包會(huì)使代碼變得很難理解和調(diào)試.我們可以用apply或者call方法改變作用域來實(shí)現(xiàn)綁定。
EventUtil.addEvent(btn, "click", function () {
return handler.handleClick.call(handler);
});
④封裝成函數(shù):
var handler = {
message: "Event handled",
handleClick: function(event){
alert(this.message);
}
};
var message = "window";
function bind(fn, context) {
return function () {
fn.call(context, arguments);
}
}
var btn = document.getElementById("btn");
EventUtil.addEvent(btn, "click", bind(handler.handleClick, handler));
⑤ECMAScript 5 為所有函數(shù)定義了一個(gè)原生的bind()方法,進(jìn)一步簡(jiǎn)單了操作.
EventUtil.addEvent(btn, "click", handler.handleClick.bind(handler));
五、函數(shù)柯里化.
1.含義:調(diào)用另一個(gè)函數(shù),并為它傳入要柯里化的函數(shù)和參數(shù)。以下函數(shù)能很好的展示庫里化的概念。
function add(num1, num2){
return num1 + num2;
}
function curriedAdd(num2){
// 調(diào)用了add()函數(shù),并為它傳入了參數(shù)
return add(5, num2);
}
alert(add(2, 3)); //5
alert(curriedAdd(3)); //8
2.創(chuàng)建函數(shù)庫里化的通用方式
function curry(fn){
// 取得外部函數(shù)的從第二個(gè)開始的所有參數(shù)
var args = Array.prototype.slice.call(arguments, 1);
// alert(args); // 10, 10, 10
return function(){
// 取得內(nèi)部函數(shù)(匿名函數(shù))的全部參數(shù)
var innerArgs = Array.prototype.slice.call(arguments);
// alert(innerArgs); // 15, 15
// 將外部的參數(shù)和內(nèi)部參數(shù)全部聚集在一起
var finalArgs = args.concat(innerArgs);
// alert(finalArgs); // 10, 10, 10, 15, 15
// 最后將所有的傳入到要調(diào)用的函數(shù)中去
return fn.apply(null, finalArgs);
};
}
function add(){
var res = 0;
for(var i = 0, len = arguments.length; i < len; i++) {
res += arguments[i];
}
return res;
}
// 對(duì)于下面的函數(shù)調(diào)用
// fn為add函數(shù)
// args為10, 10, 10
// innerArgs為15, 15
var curried = curry(add, 10, 10, 10);
alert(curried(15, 15)); // 60
3.上述的curry函數(shù)沒用改變相應(yīng)的作用域,我們可以繼續(xù)修改
function curry(fn, context){
// 取得外部函數(shù)的從第三個(gè)開始的所有參數(shù)
var args = Array.prototype.slice.call(arguments, 2);
// alert(args); // btn
return function(){
// 取得內(nèi)部函數(shù)(匿名函數(shù))的全部參數(shù)
var innerArgs = Array.prototype.slice.call(arguments);
// alert(innerArgs); // [object MouseEvent]
// 將外部的參數(shù)和內(nèi)部參數(shù)全部聚集在一起
var finalArgs = args.concat(innerArgs);
alert(finalArgs); // btn, [object MouseEvent]
// 最后將所有的傳入到要調(diào)用的函數(shù)中去
return fn.apply(context, finalArgs);
};
}
var handler = {
message: "Event handled",
handleClick: function(name, event){
alert(this.message + ":" + name + ":" + event.type);
}
};
var btn = document.getElementById("btn");
EventUtil.addEvent(btn, "click", curry(handler.handleClick, handler, "btn"));
4.ECMAScript 5 的bind()方法也實(shí)現(xiàn)函數(shù)柯里化,只要在this 的值之后再傳入另一個(gè)參數(shù)即可。
var handler = {
message: "Event handled",
handleClick: function(name, event){
alert(this.message + ":" + name + ":" + event.type);
}
};
var btn = document.getElementById("btn");
EventUtil.addEvent(btn, "click", handler.handleClick.bind(handler, "btn"));
高級(jí)函數(shù)
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
推薦閱讀更多精彩內(nèi)容
- 高級(jí)函數(shù)_分析函數(shù)與窗口函數(shù) 分析函數(shù)往往與窗口函數(shù)一起使用,over()為窗口函數(shù) 一、分析函數(shù) 1.01、排名...
- 使用 __slots__ 限制實(shí)例屬性, 比如,只允許對(duì)Student實(shí)例添加name和age屬性。 然后,我們?cè)?..
- 惰性函數(shù)很好理解,假如同一個(gè)函數(shù)被大量范圍,并且這個(gè)函數(shù)內(nèi)部又有許多判斷來來檢測(cè)函數(shù),這樣對(duì)于一個(gè)調(diào)用會(huì)浪費(fèi)時(shí)間和...
- 高階函數(shù),又稱算子(運(yùn)算符)或泛函,包含多于一個(gè)箭頭的函數(shù),高階函數(shù)是至少滿足下列一個(gè)條件的函數(shù):1.接受一個(gè)或多...