/*
函数
*/
/*
JS中的函数有两个主要特点
函数时第一类对象
可以提供作用域
函数就是对象
可以在运行时动态创建,还可以在程序执行过程中创建
函数可以分配给变量,可以将它们的引用复制到其他变量,可以被扩展,绝大多数情况下,函数还可以被删除
可以作为参数传递给其他函数,并且还可以由其它函数返回
可以有自己的属性和方法
*/
/*----------------------------START函数定义的三种方式--------------------------------------*/
/*
消除术语歧义
命名函数表达式
未命名函数表达式、函数表达式、匿名函数
函数声明
*/
/*命名函数表达式*/
var funName_1 = function funName_1(param1, param2) {
console.log(param1, param2);
};
/*未命名函数表达式,也简称函数表达式,或叫做匿名函数*/
var funName_2 = function(param1, param2) {
console.log(param1, param2);
};
var funName_3 = function specialName(param1, param2) {
console.log(param1, param2);
};
/*
上述两种函数表达式唯一区别在于该函数对象的name属性是否为一个空字符串
name属性是JS语言的一个扩展,并不属于ECMA标准的一部分
*/
console.log(funName_1.name);
console.log(funName_2.name);
try {
specialName("param1", "param2");
} catch (e) {
console.log(e.message);
}
// 输出:
// funName_1
// funName_2
// 错误:Uncaught ReferenceError: specialName is not defined
/*函数声明*/
function funName_4(param1, param2) {
console.log(param1, param2);
}
console.log(funName_4.name);
// 输出:
// funName_4
/*
函数字面量这个术语可能表示一个函数表达式或命名函数表达式,比较模糊,所以应该避免使用
*/
/*
声明Vs表达式:名称和变量声明提升
函数声明只能出现在“程序代码”中,也就是说只能在其他函数体内部或全局空间中,他们的定义不能分配给变量或是属性,也不能以参数形式出现在函数调用中
函数的name属性
只有匿名函数的name属性为空字符串或是未定义--根据具体浏览器实现,命名函数表达式和函数申明都有name属性
函数的提升
命名函数表达式和函数声明的区别在于函数提升
提升的不仅仅是函数声明,函数的定义也被提升
*/
/*命名函数表达式及匿名函数不会被提升*/
function fun1() {
console.log("In fun1");
}
function fun2() {
console.log("In fun2");
}
(function hoisting() {
console.log(fun1);
console.log(fun2);
function fun1() {
console.log("In fun1 of hoisting");
}
var fun2 = function() {
console.log("In fun2 of hoisting");
};
})();
// 输出:
// function fun1() {
// console.log("In fun1 of hoisting");
// }
// undefined
// 分析:
// hoisting函数中的fun1被提升,所以console.log(fun1)输出了hoisting中fun1函数的定义
// 仅仅变量fun2被提升,而匿名函数未被提升,所以输出的是undefined
/*----------------------------END函数定义的三种方式--------------------------------------*/
/*----------------------------START回调模式--------------------------------------*/
/*
所谓回调,就是将一个函数A传递给另外一个函数B,函数根据自己的情况在适当的时候调用函数A
回调模式中回调函数的this对象需要注意是指向全局对象的,在浏览器中也就是window对象
当回调函数是一个对象的方法时,希望回调函数中的this指向这个对象,则需要将这个对象一并传入
*/
var callbackExample = function(callback, obj) {
if (typeof callback === 'function') {
callback.call(obj);
}
};
function fun1() {
console.log(this);
}
var fun2 = function() {
console.log(this);
};
var someObj = {
fun3: function() {
console.log(this);
}
};
callbackExample(fun1);
callbackExample(fun2);
callbackExample(someObj.fun3, someObj);
// 输出:
// Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}
// Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}
// Object {fun3: function}
/*----------------------------END回调模式--------------------------------------*/
/*----------------------------START自定义函数模式--------------------------------------*/
/*
自定义函数模式
函数在函数体内部改变自己的函数定义的模式
也可以叫 惰性函数定义
缺点:
它重定义自身时已经添加到原始函数的任何属性都会丢失
如果该函数使用了不同的名称,那么重定义部分将永远不会发生,并且将会执行原始函数体
*/
var someFun = function() {
console.log("Before changing");
someFun = function() {
console.log("After changing");
};
};
someFun();
someFun();
// 输出:
// Before changing
// After changing
/*----------------------------END自定义函数模式--------------------------------------*/
/*----------------------------START即时函数模式--------------------------------------*/
/*
即时函数
在定义函数后立即执行该函数的语法
由以下几部分组成:
可以使用函数表达式定义一个函数(不能用函数声明)
在末尾添加一组括号,这将导致函数立即执行
将整个函数包装在括号中(只有不讲该函数分配给变量才需要这么做)
可以为初始化代码提供一个作用域的sandbox
将所有代码包装到它的局部作用域中,可以防止全局作用域的污染
*/
/*第一种语法*/
(function() {
console.log("Running!");
}());
/*第二种语法*/
(function() {
console.log("Running!");
})();
// 输出:
// Running!
// Running!
/*
即时函数也可以传递参数
全局对象是以参数方式传递给即时函数的,以便在不适用window:指定全局作用域限定的情况下可以在函数内部访问该对象
*/
(function(global) {
console.log(typeof global.setInterval);
}(window));
// 输出:
// function
/*
即时函数的返回值
即时函数也可以返回值,可以将这些返回值分配各变量
*/
var result = (function(firstName, lastName) {
return "My name is : " + firstName + " " + lastName;
}("Bill", "Gates"));
console.log(result);
// 输出:
// My name is : Bill Gates
/*
即时函数的优点
包装想要执行的任务,而不用担心会污染全局对象
*/
/*----------------------------END即时函数模式--------------------------------------*/
/*----------------------------START即时对象的初始化模式--------------------------------------*/
/*
即时对象的初始化模式
使用带有init()方法的对象,该方法在创建对象后将会立即执行,init()函数负责所有的初始化任务
*/
/*实例*/
var initOptions = ({
firstName: "",
lastName: "",
getWholeName: function() {
return "My name is : " + firstName + " " + lastName;
},
init: function() {
firstName = "Bill";
lastName = "Gates";
console.log(this.getWholeName());
//...
return this;
}
}).init();
console.log(initOptions);
// 输出:
// My name is : Bill Gates
// Object {firstName: "", lastName: "", getWholeName: function, init: function}
/*
这种模式主要适用于一次性的任务,如果在init中没有return this语句,将没有对该对象的访问
保护了全局命名空间
*/
/*----------------------------START初始化时分支模式--------------------------------------*/
/*
初始化分支模式
也称为加载时分支,是一种优化模式,当知道某个条件在整个程序生命周期内都不会发生改变,仅对该条件测试一次是很有意义的
*/
/*优化前浏览器嗅探实例*/
var utils = {
addListener: function(el, type, fn) {
if (typeof window.addEventListener === 'function') {
el.addEventListener(type, fn, false);
} else if (typeof document.attachEvent === 'function') {
el.attachEvent('on' + type, fn);
} else {
el['on' + type] = fn;
}
},
removeListener: function(el, type, fn) {
if (typeof window.removeEventListener === 'function') {
el.removeEventListener(type, fn, false);
} else if (typeof document.detachEvent === 'function') {
el.detachEvent('on' + type, fn);
} else {
el['on' + type] = null;
}
}
};
utils.addListener(document.body, "click", function() {
console.log("Body has been loaded");
});
// 输出:
// Body has been loaded
/*优化前,每次调动addListener和removeListener,都会进行一次浏览器嗅探的计算,效率较低*/
/*优化后浏览器嗅探实例*/
var utils = ({
addListener: null,
removeListener: null,
init: function() {
if (typeof window.addEventListener === 'function') {
this.addListener = function(el, type, fn) {
el.addEventListener(type, fn, false);
};
this.removeListener = function(el, type, fn) {
el.removeEventListener(type, fn, false);
};
} else if (typeof document.attachEvent === 'function') {
this.addListener = function(el, type, fn) {
el.attachEvent('on' + type, fn);
};
this.removeListener = function(el, type, fn) {
el.detachEvent('on' + type, fn);
};
} else {
utils.addListener = function(el, type, fn) {
el['on' + type] = fn;
};
utils.removeListener = function(el, type, fn) {
el['on' + type] = null;
};
}
return this;
}
}).init();
utils.addListener(document.body, "click", function() {
console.log("Body has been loaded");
});
// 输出:
// Body has been loaded
/*----------------------------END初始化时分支模式--------------------------------------*/
/*----------------------------START备忘模式--------------------------------------*/
/*
备忘模式
自定义属性的其中一个用途是缓存函数结果,因此在下一次调用该函数时就不用重做潜在的繁重计算。缓存函数结果也被称为备忘
*/
var someFun = function(params) {
if (!someFun.cache[params]) {
var result = {};
result.startTime = new Date().getTime();
for (var i = 100000000; i--;) {}
result.endTime = new Date().getTime();
result.message = params;
someFun.cache[params] = result;
}
return someFun.cache[params];
};
someFun.cache = {};
console.log(someFun("first"));
console.log(someFun.cache);
console.log(someFun("second"));
console.log(someFun.cache);
console.log(someFun("first"));
console.log(someFun.cache);
console.log(someFun("second"));
console.log(someFun.cache);
// 输出:
// Object {startTime: 1371903727234, endTime: 1371903727398, message: "first"}
// Object {first: Object}
// Object {startTime: 1371903727399, endTime: 1371903727557, message: "second"}
// Object {first: Object, second: Object}
// Object {startTime: 1371903727234, endTime: 1371903727398, message: "first"}
// Object {first: Object, second: Object}
// Object {startTime: 1371903727399, endTime: 1371903727557, message: "second"}
// Object {first: Object, second: Object}
// 分析:
// 第一次调用someFun("first"),由于cache中并不存在,所以进行了计算,计算的结果存入到cache中
// 第一次调用someFun("second"),由于cache中并不存在,所以进行了计算,计算的结果存入到cache中
// 第一次调用someFun("first"),由于cache中存在,所以没有进行计算,直接取出,所以startTime和endTime为第一次计算的结果
// 第一次调用someFun("second"),由于cache中存在,所以没有进行计算,直接取出,所以startTime和endTime为第一次计算的结果
/*如果有多个参数,可以使用JSON先将参数对象*/
var someFun = function(firParam, secParam) {
var cacheKey = JSON.stringify(Array.prototype.slice.call(arguments)),
result;
if (!someFun.cache[cacheKey]) {
var result = {};
result.startTime = new Date().getTime();
for (var i = 100000000; i--;) {}
result.endTime = new Date().getTime();
result.message = firParam + secParam;
someFun.cache[cacheKey] = result;
}
return someFun.cache[cacheKey];
};
someFun.cache = {};
console.log(someFun.cache);
console.log(someFun("Bill", "Gates"));
console.log(someFun.cache);
console.log(someFun("Bill", "Gates"));
// 输出:
// Object {}
// Object {startTime: 1371904542667, endTime: 1371904543172, message: "BillGates"}
// Object {["Bill","Gates"]: Object}
// Object {startTime: 1371904542667, endTime: 1371904543172, message: "BillGates"}
/*----------------------------END备忘模式--------------------------------------*/
/*----------------------------START配置对象模式--------------------------------------*/
/*
配置对象模式
一种提供更整洁的API的方法,用一个对象作为参数代替大量的参数
使用时需要传递大量的参数并不是很方便,一个更好的办法是仅使用一个参数对象来替代所有参数
优点:
不需要记住众多的参数以及其顺序
可以安全忽略可选参数
更加易于阅读和维护
更加易于添加和删除参数
缺点:
需要记住参数名称
属性名称无法被压缩
*/
/*----------------------------END配置对象模式--------------------------------------*/
/*----------------------------START Curry化--------------------------------------*/
/*
Curry化
使函数理解并处理部分应用的过程
*/
function add(x, y) {
var oldx = x,
oldy = y;
if (typeof oldy === 'undefined') {
return function(newy) {
return oldx + newy;
};
}
return x + y;
}
console.log(add(3)(4));
console.log(add(3,4));
var add3 = add(3);
console.log(add3(10));
// 输出:
// 7
// 7
// 13
/*----------------------------END Curry化--------------------------------------*/
《JavaScript模式》--第四章:函数
最新推荐文章于 2020-04-30 16:24:00 发布