Javascript函数深入掌握

来源:互联网 发布:晨曦是什么软件 编辑:程序博客网 时间:2024/06/03 19:55
var strict = (function(){ return !this}()); // 定义并调用一个函数来确定当前脚本运行时是否为严格模式 


方法是保存在对象的属性中的js函数。  方法调用和函数调用最大区别是上下文。


var o = {
m: function() {
var self = this;
console.log(this == o);// true  
f();

function f(){
console.log(this == o);// false, this为global或者undefined。
console.log(self == o);// true, 指外层的this对象。
}
}
如果嵌套函数作为函数调用,其this值不是全局对象就是undefined.

如果函数调用之前带有关键字 new,它就构成构造函数调用,构造函数调用和普通函数和方法调用在实参处理、调用上下文和返回值方面不同。
凡是没有形参的构造函数都可以省去圆括号。
构造函数调用创建一个新的对象,这个对象继承构造函数的prototype属性,构造函数试图初始化这个新建的对象,并且将新建的对象作为
调用上下文,因此构造函数可以使用this关键字来引用新建的对象。
构造函数不使用return关键字,当构造函数的函数体执行完毕,它会显式返回,如果使用return语句,则返回的是return后的对象,如果return没有指定返回对象或返回一个原始值,则将忽略返回值,同时使用这个新对象作为调用结果。

函数也是对象,函数对象也包含方法, call(), apply();


函数的形参:
可选形参:当调用函数的实参个数比函数声明的时候少,则缺少的形参都将设置为undefined,给形参赋一个默认值:
function getPropertyName(o, /* optinal */ a){
if ( a == undefined) a = [];  // 等于  a = a || [];
for(var property in o) a.push(property);
return a;
}
注意:可选形参放置在函数声明的末尾;
当传入的参数个数多余函数声明中所定义的形参的个数,没有办法直接获取多余的实参的值,参数对象刚好解决这个问题。
在函数体内,标识符arguments是指向参数对象的引用,参数对象是一个类数组对象,通过数字下标访问传入函数的实参值。
使用案例:
function max(/* ... */){
int max = Number.NEGATIVE_INFINITY;
for(var i = 0; i < arguments.length; i++)
if (arguments[i] > max) max = arguments[i];
return max;
}
除了数组元素,实参对象还有callee和caller属性,在ES5严格模式下,对这两个属性的读写都会产生类型错误。

var factorial = function(x) {
if ( x <= 1 ) return 1;
return x * arguments.callee(x-1);

将对象属性用作实参:
function easycopy(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 square(x) { return x * x }
var f = square;
square(4); // 上下两个表达式是等价的 
f(4); 

var o = {square: function(x){ return x*x }}
o.square(4);
var a = [function(x) {return x*x}, 20]//数组直接量;

函数并不是原始值,而是一种特殊对象,函数可以拥有属性。当函数需要一个“静态”变量在调用时保持某个值不变,最方便的方法是给函数
定义属性。
uniqueInteger.counter = 0; // 由于函数声明被提前,所以可以在函数声明之前给成员赋值
function uniqueInteger(){
return uniqueInteger.counter++;
}

function factorial(n){
if (isFinte(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 module(){
// 模块代码 
// 模块所使用的所有变量都是局部变量。
// 而不是污染全局命名空间
}
module();
使用匿名函数
(function(){ //模块代码 }());// function前面的左圆括号是必须的,如果不写,Javascript解释器
会将关键字function解析为函数声明语句,使用圆括号,Javascript才会正确解释为函数定义表达式,

var extend = (function(){
for(var p in {toString: null}) {
return function extend(o) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for(var prop in source) o[prop] = source[prop];
}
return o;
};

var protoprops = ["toString", "valueOf", "constructor", "hasOwnProperty", 
"isPrototypeOf", "propertyIsEnumerable", "toLocaleString"];
}
}());

闭包:
Javascript采用语法作用域,函数的执行依赖于变量的作用域,而作用域是函数定义时决定的,而不是函数调用时决定的。
为了实现这种词法作用域,Javascript函数对象的内部状态不仅包含函数的代码逻辑,也必须包含当前的作用域链


定义大多数函数是的作用域链在调用函数是依然有效,这不影响闭包,当调用函数是的作用域链和定义函数时作用域链
不是同一个作用域链时,事情就变的微妙。

理解闭包首先理解嵌套函数的词法作用域规则:
var scope = "global scope";// 全局变量
function checkscope(){
var scope = "local scope";// 局部变量
function f() {return scope;}//在作用域中返回这个值
return f();
}

checkscope(); // => "local scope"

var scope = "global scope";// 全局变量
function checkscope(){
var scope = "local scope";// 局部变量
function f() {return scope;}//在作用域中返回这个值
return f;
}

checkscope()();// => "依然是local scope"


var uniqueInteger = (function{
var counter = 0;
return function(){ return counter++; };
}();