摘要:在定义函数的作用域外调用,得到的返回仍然是函数创建时所在的作用域的局部变量。这是因为所在的匿名函数的闭包中存放的是第一行的,而不是在循环中获得的的当前值。
原文: http://pij.robinqu.me/JavaScript_Core/JavaScript_Basics/Function.html
源代码: https://github.com/RobinQu/Programing-In-Javascript/blob/master/chapters/JavaScript_Core/JavaScript_Basics/Function.md
本文存在批注,但该网站的Markdown编辑器不支持,所以无法正常展示,请到原文参考。
函数Javascript中,要记住函数是first-class citizen。
定义
函数声明语句
function plus(x ,y) { }
函数定义表达式
var plus = function (x, y) { }函数调用
作为函数调用
function a(){}; a();
作为方法调用
a={}; a.x = function(){}; a.x();
通过call和apply间接调用函数(改变this)
call 和 apply带有多个参数,call和apply把当前函数的this指向第一个参数给定的函数或对象中,并传递其余所有的参数作为当前函数的参数。
var O = function () { this.foo = "hello"; this.hello = function () { return "world"; } }; var fn = function () { console.log("call", this); }; var o = new O(); fn.call(o);//此时fn的this指向o
call和apply的不同之处,在于call传递的参数是作为arguments依次传入的,例如
fn.call(o, 1, 2, 3);
而apply传递的参数是以一个数组的方式传入的,例如
fn.apply(o, [1, 2, 3]);
当传入参数少于函数声明的参数时,留空的参数的值是undefined。
Javascript允许传入参数的个数大于声明时制定的参数个数。可以用arguments来访问这些参数
function f(){ var i; for( i = 0; i < arguments.length ; i++) { console.log(arguments[i]); } } f(1,2,3,4,5,6);
函数通过取得arguments的长度得到传入参数的个数,使用一个循环获取每一个参数。
arguments还有两个属性,callee和caller
callee表示正在执行的function对象,
caller表示调用当前function的function
例如
function f(){ console.log(arguments.callee);//[Function: f] console.log(arguments.callee.caller);[Function: g] var i; for( i = 0; i < arguments.length ; i++) { console.log(arguments[i]); } } function g(){ f(1,2,3,4,5,6); } g();
callee 的重要用法之一是在匿名函数中实现递归
var result = function (x) { if (x <= 1) return 1; return x * arguments.callee(x - 1); }(3); console.log(result);
上例使用了一个匿名函数和callee实现了一个阶乘。
作为值的函数javascript中的函数可以作为值来传递
function square(x) { return x * x; } var s = square; s(4);作为命名空间的函数
(function() { }());闭包
Javascript函数对象的内部状态不仅包含着函数的代码逻辑,还引用当前的作用域链。函数对象通过作用域链相互关联起来,函数体内部变量包含在函数作用域内,这就叫闭包。
例如
var scope = "global scope"; function checkscope() { var scope = "local scope"; function f() { return scope; } return f; } checkscope()();
这段checkscope声明了一个局部变量,定义了一个函数f,函数f返回了这个局部变量的值,最后返回了这个函数f。在定义函数f的作用域外调用f,得到的返回仍然是函数f创建时所在的作用域的局部变量scope。
又例如
var counter = (function() { var count = 0; return function () { return count++ ; } }());
代码定义了一个立即执行函数并返回给counter,这个函数定义了一个局部变量count,返回了一个子函数,该子函数每次调用,都会吧count加一并返回。
闭包的注意事项
观察下面的示例:
var add_the_handlers = function (nodes) { var i; for (i = 0; i < nodes.length; i += 1) { nodes[i].onclick = function (e) { alert(i); }; } };
这个函数期望的结果,是在运行的时候为每个node在onclick的时候alert出各自的序号,但是实际运行的结果却不同:所有的node在单击的时候alert出来的数字总是同一个。
这是因为alert所在的匿名函数的闭包中存放的i是第一行的i,而不是在循环中获得的i的当前值。
所以如果希望达到预期结果,应该在循环中创建多个闭包,在闭包中存放当前循环的i的值:
var add_the_handlers = function (nodes) { var i; for (i = 0; i < nodes.length; i += 1) { nodes[i].onclick = function (i) { return function(e){ alert(e); }; }(i); } };
这里使用一个立即执行函数并传递当前的i的值,返回一个新生成的函数。在这个新生成的函数的闭包中就保存了当前的i的值。
函数中的this对象在一个对象中的this始终引用当前对象,但是在函数中,特别是在闭包中,this有一些特殊的行为。
函数中的this对象始终绑定在函数运行时的上下文环境上。所以在普通模式下调用一个全局函数,this始终指向window(客户端),在严格模式下调用一个全局函数,this始终是undefined
示例
var name = "The Window"; var object = { name: "My Object", getNameFunc: function () { return function () { return this.name; }; }, getName : function () { return this.name; } }; console.log(object.getNameFunc()()); console.log(object.getName());
getNameFunction()返回了一个匿名函数,这个匿名函数在调用的时候,上下文是window(浏览器中),所以在浏览器中输出的是the Window
而getName()调用的时候上下文是object,所以成功输出object的name
其实以上代码中
object.getNameFunc()()
等效于
var fnc = object.getNameFunc();//这时候的fnc已经脱离了object对象
fnc();
所以如果想要getNameFunction()正确返回Object的Name,需要在返回的匿名函数的闭包中保存在函数声明时的this,
getNameFunc: function () { var that = this; return function () { return that.name; }; },
这样就可以了。。
函数柯里化函数柯里化是指,把接受多个参数的函数转换成接受一个单一参数的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
示例
var add1 = add.curry(1); console.log(add1(2));
其中,add是接受两个参数的函数,add调用了curry返回一个只接受一个参数的新函数,之后调用add1便等效于调用add(1, 2);
javascript并不原生支持curry,可以用prototype来模拟
Function.prototype.curry = function () { var slice = Array.prototype.slice, args = slice.apply(arguments), that = this; return function () { return that.apply(null, args.concat(slice.apply(arguments))); }; }; function add(n1, n2) { return n1 + n2; } var add1 = add.curry(1); console.log(add1(2));
curry创建了一个新函数,在新函数的闭包中保存了原先传递的参数。
函数的属性和方法length 函数的length表示函数实参的数量,是只读的
prototype 指向一个该函数的原型对象的引用
toString 返回一个字符串
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/78111.html
摘要:基础语法区分大小写是一种区分大小写的语法,意味着的关键字变量名函数名以及其他一切的字符表示都要使用一致的大小写形式。化物语化物语空格和换行会忽略代码中出现的空格换行制表符。如果不用花括号独立独立编写一个语句,语法并不报错,但不推荐。 JavaScript基础语法 区分大小写 JavaScript是一种区分大小写的语法,意味着JavaScript的关键字、变量名、函数名以及其他一切的字符...
摘要:数组创建数组数组字面量使用构造函数数组本质上是所以要判断是不是数组,需要通过判断。数组长度使用属性获取元素的个数。例如函数的对象就是这样 原文: http://pij.robinqu.me/JavaScript_Core/JavaScript_Basics/Array.html 源代码: https://github.com/RobinQu/Programing-In-...
摘要:创建对象对象直接量构造函数原型继承类继承对象拥有自有属性和继承属性。遍历顺序是以广度优先遍历所以使用便可以判断是否是对象自有的属性。可执行对象通过如下方法可以创建一个可执行对象既可以当作对象来使用有原型链,也可以当作函数来直接调用 原文: http://pij.robinqu.me/Javascript_Core/Javascript_Basics/Objects.html ...
摘要:多数运算符都是由标点符号表示,比如和。通常会根据需要对操作数进行类型转换左值是一个古老的属于,它是指表达式只能出现在赋值运算符的左侧。也称为严格相等运算符,它用来检测两个操作数是否严格相等。运算符的检测规则是和运算符的求反。 源代码: https://github.com/RobinQu/Programing-In-Javascript/blob/master/chapters/...
摘要:浏览器只是实现的宿主环境之一,其他宿主环境包括和。年月,版发布,成为国际标准。事件定义了事件和事件处理的接口。对于已经正式纳入标准的来说,尽管各浏览器都实现了某些众所周知的共同特性,但其他特性还是会因浏览器而异。 JavaScript 是面向 Web 的编程语言,绝大多数现代网站都使用了 JavaScript,并且所有的现代 Web 浏览器(电脑,手机,平板)均包含了 JavaScri...
阅读 2432·2021-11-15 11:36
阅读 1170·2019-08-30 15:56
阅读 2242·2019-08-30 15:53
阅读 1037·2019-08-30 15:44
阅读 648·2019-08-30 14:13
阅读 996·2019-08-30 10:58
阅读 475·2019-08-29 15:35
阅读 1292·2019-08-29 13:58