Javascript Good Part读书笔记-JavaScript函数
文章目录
函数(Functions)
函数对象(Function Objects)
在JavaScript中函数就是对象(Functions in Javascript are objects),通过字面符产生的对象指向Object.prototype,函数对象指向Function.prototype,Function.prototype指向Object.prototype。每个函数被创建后带有一个prototype属性,prototype属性的值是一个包含constructor属性的对象,而constructor属性的值是该函数(function)。
函数字面符号(Function Literal)
通过字面符号创建函数对象。
1 2 3 |
var add = function (a, b) {
return a + b;
}; |
调用(Invocation)
每个函数接受两个额外的参数:this, arguments。可通过4种模式调用函数。
- 方法调用(method invocation)
当一个函数被赋值/存储为一个对象属性,文件叫它为一个方法。当一个方法调用时this被绑定到这个对象上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// Create myObject. It has a value and an increment
// method. The increment method takes an optional
// parameter. If the argument is not a number, then 1
// is used as the default.
var myObject = {
value: 0,
increment: function (inc) {
this.value += typeof inc === 'number' ? inc : 1;
}
};
myObject.increment();
document.writeln(myObject.value); // 1
myObject.increment(2);
document.writeln(myObject.value); // 3 |
- 函数调用(function invocation)
一个函数不是一个对象属性是,被当作函数调用,此时this被绑定到global对象。当内部函数被调用时,this仍然被绑定到了外部函数。
1 2 3 4 5 6 7 8 9 10 |
myObject.double = function () {
var that = this; // Workaround.
var helper = function () {
that.value = add(that.value, that.value);
};
helper(); // Invoke helper as a function.
};
// Invoke double as a method.
myObject.double( );
document.writeln(myObject.value); |
- 构造函数调用(constructor invocation)
当一个函数通过new 前缀调用,一个新的对象将被创建,this与新创建的对象绑定。
当一个函数被定义使用new调用时,被叫做构造器(constructor),习惯上首字母大写,用于与普通函数区分。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// Create a constructor function called Quo.
// It makes an object with a status property.
var Quo = function (string) {
this.status = string;
};
// Give all instances of Quo a public method
// called get_status.
Quo.prototype.get_status = function () {
return this.status;
};
// Make an instance of Quo.
var myQuo = new Quo("confused");
document.writeln(myQuo.get_status()); // confused |
- apply调用(apply invocation)
apply方法包括两个参数,第一个参数用于绑定this,第二个参数是一个数组。
1 2 3 4 5 6 7 8 9 10 |
// Make an array of 2 numbers and add them.
var array = [3, 4];
var sum = add.apply(null, array); // sum is 7
// Make an object with a status member.
var statusObject = {
status: 'A-OK'
};
// statusObject does not inherit from Quo.prototype, // but we can invoke the get_status method on
// statusObject even though statusObject does not have // a get_status method.
var status = Quo.prototype.get_status.apply(statusObject); // status is 'A-OK' |
参数(Arguments)
每个函数被调用时都有一个隐式的arguments参数,包含了调用的参数,可以通过类似数组的方式访问,但是arguments 不是一个真正的数组(只有length属性,没有数组的任何方法)。
返回值(Return)
一个函数总会返回一个值,如果没有指定则返回undefined。如果一个函数通过new的方式调用返回this(new的对象)。
异常(Exceptions)
throw语句会打断一个函数的执行,一个exception对象包括一个name属性(唯一标示一个异常类型)和一个message属性(异常描述)。
1 2 3 4 5 6 7 8 9 |
var add = function (a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw {
name: 'TypeError',
message: 'add needs numbers'
};
}
return a + b;
} |
try语句只有一个catch块(捕获所有的异常),如果有多种类型的异常需哟处理,可根据异常名称确定异常的类型。
1 2 3 4 5 6 7 8 |
var try_it = function () {
try {
add("seven");
} catch (e) {
document.writeln(e.name + ': ' + e.message);
}
}
try_it( ); |
扩展类型功能
在JavaScript中可以对基本类型进行功能扩展。对Object.prototype添加方法对所有对象都可用,包括:函数(functions)、数组(arrays)、字符串(strings)、数字(numbers)、正则表达式(regular expressions)、布尔(booleans)。
通过Function.prototype添加方式对所有的函数都有效。
1 2 3 4 |
Function.prototype.method = function (name, func) {
this.prototype[name] = func;
return this;
}; |
1 2 3 4 5 6 7 |
// Add a method conditionally.
Function.prototype.method = function (name, func) {
if (!this.prototype[name]) {
this.prototype[name] = func;
return this;
}
}; |
添加整数方法到Number
1 2 3 |
Number.method('integer', function () {
return Math[this < 0 ? 'ceil' : 'floor'](this);
}); |
1
|
document.writeln((-10 / 3).integer()); // -3 |
添加删除空格的方法到string
1 2 3 |
String.method('trim', function () {
return this.replace(/^\s+|\s+$/g, '');
}); |
1
|
document.writeln('"' + " neat ".trim() + '"'); |
递归(Recursion)
尾部递归优化
1 2 3 4 5 6 7 8 9 10 11 12 |
// Make a factorial function with tail
// recursion. It is tail recursive because
// it returns the result of calling itself.
// JavaScript does not currently optimize this form.
var factorial = function factorial(i, a) {
a = a || 1;
if (i < 2) {
return a;
}
return factorial(i - 1, a * i);
};
document.writeln(factorial(4)); // 24 |
作用域(Scope)
JavaScript只有函数作用域,没有块作用域。最好在函数体的顶部声明变量。
1 2 3 4 5 6 7 8 9 10 11 12 |
var = function () {
a = 3, b = 5;
var bar = function () {
var b = 7, c = 11;
// At this point, a is 3, b is 7, and c is 11
a += b + c;
// At this point, a is 21, b is 7, and c is 11
};
// At this point, a is 3, b is 5, and c is not defined
bar();
// At this point, a is 21, b is 5
}; |
闭包(Closure)
内部函数可以访问定义它的外部函数的参数和变量(this和arguments除外)。
通过闭包实现封装,保护不想让外部程序访问的属性。
1 2 3 4 5 6 7 8 9 10 11 12 |
// 不是赋值一个函数给对象,而是赋值一个函数调用的返回值。
var myObject = (function () {
var value = 0;
return {
increment: function (inc) {
value += typeof inc === 'number' ? inc : 1;
},
getValue: function () {
return value;
}
};
}()); |
1 2 3 4 5 6 7 8 9 10 |
var quo = function (status) {
return {
get_status: function () { return status;
}
};
};
// Make an instance of quo.
var myQuo = quo("amazed");
document.writeln(myQuo.get_status( )); |
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// Define a function that sets a DOM node's color
// to yellow and then fades it to white.
var fade = function (node) {
var level = 1;
var step = function () {
var hex = level.toString(16);
node.style.backgroundColor = '#FFFF' + hex + hex;
if (level < 15) {
level += 1;
setTimeout(step, 100);
}
};
setTimeout(step, 100);
};
fade(document.body); |
正例(避免在循环中创建函数)
1 2 3 4 5 6 7 8 9 10 11 |
var add_the_handlers = function (nodes) {
var helper = function (i) {
return function (e) {
alert(i);
};
};
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = helper(i);
}
}; |
反例(弹出框提示总是最后一个值)
1 2 3 4 5 6 7 8 |
var add_the_handlers = function (nodes) {
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function (e) {
alert(i);
};
}
}; |
回调(Callbacks)
在异步请求中传递一个函数作为参数,当响应返回时该函数被调用。
1 2 3 |
request = prepare_the_request(); response = send_request_synchronously(request); display(response); |
使用回调方式
1 2 3 |
request = prepare_the_request(); send_request_asynchronously(request, function (response) {
display(response);
}); |
模块化(Module)
我们可以使用函数和闭包实现模块化。一个模块是一个函数或对象的接口表现,隐藏了具体的实现和状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
String.method('deentityify', function () {
// The entity table. It maps entity names to characters.
var entity = {
quot: '"',
lt: '<',
gt: '>'
};
// Return the deentityify method.
return function () {
// This is the deentityify method. It calls the string
// replace method, looking for substrings that start
// with '&' and end with ';'. If the characters in
// between are in the entity table, then replace the
// entity with the character from the table.
return this.replace(/&([^&;]+);/g, function (a, b) {
var r = entity[b];
return typeof r === 'string' ? r : a;
});
};
}()); |
1
|
document.writeln( '<">'.deentityify()); // <"> |
及联调用(Cascade)
如果一个函数返回this则可以使用及联调用。
柯里化(Curry)
柯里化允许我们通过组合一个函数和一个参数产生一个新的函数。JavaScript没有柯里化方法,我们可以通过给Function.prototpe添加一个方法实现。
1 2 3 4 5 6 7 8 |
Function.method('curry', function () {
var slice = Array.prototype.slice,
args = slice.apply(arguments),
that = this;
return function () {
return that.apply(null, args.concat(slice.apply(arguments)));
};
}); |
1 2 |
var add1 = add.curry(1); document.writeln(add1(6)); // 7 |
内存化(Memoization)
函数使用对象保存前一个操作的结果,从而避免不必要的重复计算。
1 2 3 4 5 6 7 |
var fibonacci = function (n) {
return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
};
for (var i = 0; i <= 10; i += 1) {
document.writeln('// ' + i + ': ' + fibonacci(i));
} |
内存化后
1 2 3 4 5 6 7 8 9 10 11 12 |
var fibonacci = (function () {
var memo = [0, 1];
var fib = function (n) {
var result = memo[n];
if (typeof result !== 'number') {
result = fib(n - 1) + fib(n - 2);
memo[n] = result;
}
return result;
};
return fib;
}()); |
提出函数
1 2 3 4 5 6 7 8 9 10 11 |
var memoizer = function (memo, formula) {
var recur = function (n) {
var result = memo[n];
if (typeof result !== 'number') {
result = formula(recur, n);
memo[n] = result;
}
return result;
};
return recur;
}; |
1 2 3 4 5 6 7 |
var fibonacci = memoizer([0, 1], function (recur, n) {
return recur(n - 1) + recur(n - 2);
});
var factorial = memoizer([1, 1], function (recur, n) {
return n * recur(n - 1);
}); |
文章作者 binbin wen
上次更新 2019-11-14