- 函数在javascript里面是对象。每个函数在创建时会附加两个属性:函数的上下文和实现函数行为的代码。
- 每个函数在创建的时候会有一个prototype属性,它的值是一个拥有constructor属性且值为该函数的对象。
- 函数是对象,可以像值一样被使用。函数可以保存在变量、对象和数组中。函数可以当做参数传递给其他函数,函数也可以返回函数。因为函数是对象所以函数也可以拥有方法。
- 函数的不同之处在于它可以被调用。
函数字面量
- 保留字 function
- 函数名,可以被省略。函数可以利用名字来递归调用自己。如果没有名字,则称之为匿名函数。
- 参数。
- 函数体,在函数被调用的时候执行。
函数可以被定义在其他函数中,同事它就能访问把它嵌套在中的父函数的属性。通过函数字面量创建的函数对象包含一个链接到外部的上下文。这被称之为闭包。
调用
- 调用一个函数时,会暂停当前函数的执行,传递控制权和参数给当前函数。
- 除了传递的参数之外,每个函数还接受两个附加的参数:this和 arguments。
- this的值取决于调用的模式。在js中有四种调用模式:
- 方法调用模式
当一个函数被保存为一个对象属性时,我们称之为方法。当一个方法被调用时,this被绑定到该对象。如果表达式包含一个 . 的动作,那么它就是被当做一个方法来调用,方法调用模式的this指向对象。
var myObject = {
value: 0,
increment: function (inc) {
this.value += typeof inc === 'number' ? inc : 1;
}
};
myObject.increment(1);
document.wirteln(myObject.value);
myObject.increment(2);
document.wirteln(myObject.value);
- 函数调用模式
当一个函数并非一个对象属性时,那么它就是被当做函数调用。
var sum = add(3, 4);
这时,this被绑定到全局对象上。如果语言设计正确,那么 当内部函数被调用时,this 应该任然绑定到外部函数的this变量。
但是
function a(){
var b = function() {
console.info(1231231, this);
}
b();
}
a();// 输出windows this 被绑定到外部去了
// 解决方法
var myObject = {};
myObject.fn = function(){
var that = this;
var helper = function () {
that.value = add(that.value, this.value);
}
helper();
}
//解决方法2 箭头函数的this指向 外部函数的this
var myObject = {};
myObject.fn = function() {
var b = () =>{
console.info(1231231, this);
}
b();
}
- 构造器调用模式
构造器调用模式this将会指向new出来的对象。
var Quo = function (string){
this.status = string;
}
Quo.prototype.get_status = function () {
return this.status;
}
var myQuo = new Quo("confused");
document.writeln(myQuo.get_status());
- apply调用模式(还有call调用模式)
构造一个参数数组传递给调用的函数,允许我们选择this的值。apply接受两个值 一个是this,另外一个是参数数组。
这些模式在初始化关键参数this上存在差异。
参数
当函数被调用时,会有一个arguments数组。可以编写一个无需指定参数个数的函数。
var sum = function (){
var i, sum = 0;
for (i = 0; i < arguments.length; i += 1) {
sum += arguments[i];
}
return sum;
}
arguments并不是一个数组 只是一个类似数组的对象。只有length属性,没有其他数组方法。
返回
一个函数总会有一个返回值,如果没有那么返回undefined。
如果调用时加了new,且返回值不是对象那么返回新对象即this。
异常
var add = function (a, b) {
if (typeof a !== 'number' || typeof b !== 'number' ) {
throw{
name: 'TypeError',
message: 'add needs number'
};
}
return a + b;
}
var try_it = function () {
try {
add("seven");
} catch(e) {
document.writeln(e.name + ':' + e.message);
}
}
扩充类型的功能
Function.prototype.method = function(name, func){
this.prototype[name] = func;
return this;
}
Number.method('integer', function(){
return Math[this < 0? 'ceil' : 'floor'](this);
})
document.writeln((-10/3).integer());
String.method('trim', function () {
return this.replace(/^\s+|\s+$/g, '');
})
document.writeln('"' + " neat ".trim() + '"');
符合条件时才添加方法
Function.prototype.method = function (name, func) {
if (!this.prototype[name]) {
this.prototype[name] = func;
}
return this;
}
递归 todo
作用域
var foo = function () {
var a = 3, b = 5;
var bar = function () {
var b = 7, c = 11;
a += b + c;
};
//a为3 b 为5
bar();
//a为21 b 为5
}
了解js奇怪的作用域。
闭包
函数可以访问它被创建时所处的上下文环境。这杯称之为闭包。
var fade = function(node) {
var level = 1;
var step = function () {
var hex = level.toString(16);
node.style.backgroundColor = '#FFFF' + hex + hex;
console.info('#FFFF' + hex + hex);
if(level < 15){
level += 1;
setTimeout(step, 1000);
}
}
setTimeout(step, 1000);
}
fade(document.body);
只要step还需要父函数里的变量那么这个变量就不会释放。
回调
模块
我们可以使用函数和闭包来构造模块,模块是一个提供接口却隐藏状态与实现的函数或对象。通过函数产生模块,我们可以摒弃全局变量的使用,从而缓解js的糟糕特性。
定义一个方法替换寻找字符串中的HTML实体并把他们替换为对应的字符。
String.method('deentityify', function() {
//需要替换的字符实体保存在全局变量里太污染,保留在函数里每次需要求值,但是保留在闭包里很理想
var entity = {
quot: '"',
lt: '<',
gt: '>'
};
return function() {
return this.replace(/&([^&;]+);/g,
function (a, b) {
var r = entity[b];
return typeof r === 'string' ? r : a;
}
}
})
模块的一般形式就是利用闭包创建可以访问的私有变量和函数的特权函数,最后返回这个特权函数,或者把他们保存到一个可以访问到的地方。
使用模块摒弃了全局变量的使用。它促进了信息隐藏和其他优秀的设计实践。对于应用的封装或者构建其他单例对象,模块模式非常有效。
模块也可以产生安全的对象,假定我们要构造一个用来生产序列的对象。
var serial_maker = function () {
var prefix = '';
var seq = 0;
return {
set_prefix: function (p) {
prefix = String(p);
},
set_seq: function (s) {
seq = s;
},
gensym: function () {
var result = prefix + seq;
seq += 1;
return result;
}
};
};
var seqer = serial_maker();
seqer.set_prefix('Q');
seqer.set_seq(1000);
var unique = seqer.gensym();
级联
一些方法没有返回值,一些这只或修改对象的某个状态却不返回任何值得方法就是经典的例子。如果我们让这些方法返回this而不是undefinded那么就可以启动级联。
getElement('myBoxDiv')
.move(350, 150)
.width(100)
.height(100)
.color('red')
柯里化
函数也是值,从而我们可以利用有趣的方式操作函数值。柯里化允许我们把函数与传递给它的参数相结合,产生出一个新的函数。
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)));
}
})
记忆 todo
函数可以将先前操作的结果记录在某个对象里,从而避免无所谓的重复运算。
网友评论