javascript权威指南-函数-上
作者:gcbeen
日期:2013年10月26日
一、函数定义
function printprops(o) {
var p;
for (p in o) {
console.log('p-o'); //没有返回值的函数有时称之为过程
}
}
function distance(x1, y1, x2, y2) {
var dx = x2 - x1;
var dy = y2 - y1;
return Math.sqrt(dx * dx + dy * dy);
}
function factorial(x) {
if (x <= 1) {
return 1;
}
return x * factorial(x - 1);
}
// 栈结构代替函数递归。
function factorial(x) {
var stackArray = [],
sum = 1;
while(x >= 1 || stackArray.length != 0) {
if (x > 1) {
stackArray.push(x);
} else {
// 出栈
sum = sum * stackArray.pop();
}
}
}
var square = function (x) {
return x * x;
}
var f = function fact(x) {
if (x <= 1) {
return 1;
} else {
return x * fact(x - 1);
}
}
data.sort(function(a, b) {
return a - b;
});
var tensquared = (function (x) {
return x * x;
} (10));
函数命名
- link_this
- linkThis:
- _linkThis:私有方法
以表达式方式定义的函数,函数的名称是可选的。一条函数声明语句实际上声明了一个变量,并把一个函数对象赋值给它。相对而言,定义函数表达式时并没有声明一个变量。
- 函数声明语句“被提前”到外部脚本或外部函数作用域的顶部。所以以这种方式声明的函数,可以被在它定义之前出现的代码所调用。
- 以表达式定义的函数在函数定义之前无法调用。
- 没有返回值的函数有时候称之为过程。
二、嵌套函数
function hypotenuse(a, b) {
function square(x) {
return x * x;
}
return Math.sqrt(square(a) + square(b));
}
三、函数调用
调用Javascript函数的4种方式:
- 作为函数调用
printprops({x: 1});
var total = distance(0, 0, 2, 1) + distance(2, 1, 3, 5);
var probability = factorial(5) / factorial(13);
调用上下文(this的值)是全局变量。在严格模式下,调用上下文则是undefined。
var strict = (function () { return !this; });
- 作为方法调用
o.m = f;
o.m();
o.m(x, y);
对象o成为调用上下文,函数体可以使用关键字this引用该对象。
var calculator = {
operand1: 1,
operand2: 1,
add: function () {
this.result = this.operand1 + this.operand2;
}
};
calculator.add();
calculator.result;
o["m"](x, y);
a[0](z);
customer.surname.toUpperCase();
f().m();
方法和this关键字是面向对象编程范例的核心。任何函数只要作为方法调用实际上都会传入一个隐式的实参(对象),方法调用的母体就是这个对象。
rect.setSize(width, height);
setRectSize(rect, width, height);
方法链:当方法的返回值是一个对象,这个对象还可以再调用它的方法。这种方法调用序列成为链。
$(":header").map(function () { return this.id; }).get().sort();
当方法不需要返回值时,最好直接返回this。如果设计的API中一直采用这种方式,使用API就可以进行“链式调用”风格编程。
shape.setX(100).setY(100).setSize(50).setOutline("red").setFill("blue").draw();
嵌套函数
- 如果嵌套函数作为函数调用,其this值不是全局对象就是undefined,不是调用外层函数的上下文,如果想访问外部函数的this值,需要将this的值保存在一个变量里。
- 如果嵌套函数作为方法调用,其this的值指向调用它的对象。
var o = {
m: function () {
var self = this;
console.log(this === o);
f();
function f() {
console.log(this === o);
console.log(self === o);
}
}
};
o.m();
作为构造函数 如果函数或者方法调用之前带有关键字new,他就构成构造函数调用。构造函数调用创建一个新的空对象,这个对象继承自构造函数的prototype属性。构造函数试图初始化这个新创建的对象,并将这个对象用做其调用上下文,构造函数的this关键字引用这个新创建的对象。构造函数通常不使用return关键字。
通过它们的call()和apply()方法间接调用
四、函数的实参和形参
// 可选参数
function getPropertyNames(o, /*optional*/ a) {
var property;
a = a || [];
for (property in o) {
a.push(property);
}
return a;
}
var a = getPropertyNames(o);
getPropertyNames(P, a);
// 可变长实参列表(实参对象)
function f(x, y, z) {
if (arguments.length != 3) {
throw new Error("function f called with " + arguments.length +
"arguments, but it expects 3 arguments.");
}
}
function max() {
var max = Number.NEGATIVE_INFINITY,
i,
argsLen = arguments.length;
for (i = 0; i < argsLen; i += 1) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
}
var largest = max(1, 10, 100, 2, 3, 1000, 4, 5, 10000, 6);
arguments并不是真正的数组,它是一个实参对象。以数字为索引的一组元素以及length属性。
function f(x) {
console.log(x);
arguments[0] = null;
console.log(x); // null
}
calle和caller属性
- ECMAScript标准规范规定calle属性指代当前正在执行的函数。
- caller是非标准的,大多数浏览器都实现了这个属性,指代调用当前正在执行的函数的函数。通过caller属性可以访问调用栈。
var factorial = function (x) {
if (x <= 1) {
return 1;
}
return x * arguments.callee(x - 1);
}
将对象属性用作实参。
function arraycopy(/* array */ from, /* index */ from_start, /* array */ to, /* index */ to_start, /* integer */ length) {
}
function easecopy(args) {
arraycopy(args.from, args.from_start || 0, args.to, args.to_start || 0, args.length);
}
var a = [1, 2, 3, 4], b = [];
easycopy({from: a, to: b, length: 4});
实参类型检查
function sum(a) {
if (isArrayLike(a) ) {
var total = 0,
i = 0,
element,
len = a.length;
while (i < len) {
element = a[i];
if (element == null) {
continue;
}
if (isFinite(element) ) {
total += element;
} else {
throw new Error("sum(): elements must be finite numbers");
}
i += 1;
}
return total;
} else {
throw new Error("sum(): argument must be array-like");
}
}
function flexisum(a) {
var total = 0,
i = 0,
len = arguments.length,
element,
n;
while (i < len) {
element = arguments[i];
if (element == null) {
continue;
}
if (isArray(element) ) {
n = flexisum.apply(this, element);
} else if (typeof element === "function") {
n = Number(element() );
} else {
n = Number(element);
}
if (isNaN(n) ) {
throw Error("flexisum(): can't convert " + element + " to number");
}
total += n;
}
return total;
}
五、作为值的函数
- 赋值给变量
function square(x) {
return x * x;
}
var s = square;
square(4);
s(4);
- 作为对象的属性
var o = {square: function (x) {
return x * x;
} };
var y = o.square(16);
- 赋值给数组元素
var a = [function (x) { return x * x; }, 20];
a[0](a[1]);
function add(x, y) {
return x + y;
}
function subtract(x, y) {
return x - y;
}
function multiply(x, y) {
return x * y;
}
function divide(x, y) {
return x / y;
}
function operate(operator, operand1, operand2) {
return operator(operand1, operand2);
}
var i = operate(add, operate(add, 2, 3), operate(multiply, 4, 5));
var operators = {
add: function (x, y) {
return x + y;
},
subtract: function (x, y) {
return x - y;
},
multiply: function (x, y) {
return x * y;
},
divide: function (x, y) {
return x / y;
},
pow: Math.pow
};
function operate2(operation, operand1, operand2) {
if (typeof operators[operation] === "function") {
return operators[operation](operand1, operand2);
} else {
throw "unknown operator";
}
}
var j = operate2("add", "hello", operate2("add", " ", "world") );
var k = operate2("pow", 10, 2);
六、自定义函数的属性
// 由于函数声明被提前,因此这里可以在函数声明之前给它的成员赋值。
uniqueInteger.counter = 0;
function uniqueInteger() {
return uniqueInteger.counter += 1;
}
function factorial(n) {
if (isFinite(n) && n > 0 && n == Math.round(n) ) {
if (!(n in factorial) ) {
factorial[n] = n * factorial(n - 1);
}
return factorial[n];
} else {
return NaN;
}
}
factorial[1] = 1; //将函数自身当作数组对待。
function factorial2(n) {
var stackArray = [],
sum = 1;
if (isFinite(n) && n > 0 && n == Math.round(n) ) {
while (n > 0 || stackArray.length > 0 ) {
if (n > 0) {
stackArray.push(n);
n -= 1;
} else {
sum = sum * stackArray.pop();
}
}
return sum;
} else {
return NaN;
}
}
七、作为命名空间的函数
function mymodule() {
// 模块代码
// 局部变量
}
mymodule();
// 立即执行函数。
(function () {
// 模块代码
} ());
var extend = (function () {
var p;
for (p in {toString: null}) {
return function extend(o) {
var i = 1,
len = arguments.length,
source,
prop;
while (i < len) {
source = arguments[i];
for (prop in source) {
o[prop] = source[prop];
}
}
return o;
}
}
return function patched_extend(o) {
var i = 0,
j = 0,
len = arguments.length,
propLen = protoprops.length,
source,
prop;
while (i < len) {
source = arguments[i];
for (prop in source) {
o[prop] = source[prop];
}
while (j < propLen) {
prop = protoprops[j];
if (souce.hasOwnProperty(prop) ) {
o[prop] = source[prop];
}
}
}
return o;
};
var protoprops = [ "toString", "valueOf", "constructor", "hasOwnProperty",
"isPrototypeOf", "propertyIsEnumerable", "toLocaleString" ];
} ());
blog comments powered by Disqus