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