javascript 函数和作用域(函数,this)(六)
重点。
一、函数
1、函数介绍
函数是一块JavaScript代码,被定义一次,但可执行和调用多次。JS中的函数也是对象,所以JS函数可以像其他对象那样操作和传递,所以我们也常叫JS中的函数为函数对象。
注意:
返回Return
return语句可以使函数提前返回。
一个函数总会返回一个值,函数的返回值,依赖于return语句。
一般的函数调用:如果没有return语句的话,默认会在所有代码执行完以后返回undefined。
如果是作为构造器,外部使用new去调用的话,如果没有return语句,或者return的是基本类型的话,默认会将this作为返回。
反之,如果return的是对象,将这个对象作为new构造器的返回值。
2、函数的属性和方法【20170501】
函数属性
- length属性
- prototype属性
- 自定义属性
函数方法
- call()
- apply()
3、函数的相关内容
函数内容较多,重点有:
- this
- arguments
- 作用域
- 不同调用方式
- 直接调用foo()
- 对象方法o.method()
- 构造器new Foo()
- call/apply/bind调用 func.call(o)
- 不同创建方法
二、函数声明和表达式
1、函数声明
- function add(a,b){
- a=+a;
- b=+b;
- if(isNaN(a)||isNaN(b)){
- return;
- }
- return a+b;
- }
一个完整的语句以function开头,也不加括号,也不加叹号,也不会把它括起来,也不会把它作为赋值语句的右值等待。这样定义的函数就叫函数声明。
2、函数表达式
1、 函数变量
函数表达式赋值给变量。
- //函数变量 function variable
- var add=function(a,b){
- //do sth
- }
2、立即执行函数表达式(IEF)
把一个匿名函数用括号括起来,再去直接调用。
- //立即执行函数表达式 IEF(Immediately Executed Function)
- (function(){
- })();
3、作为返回值的函数表达式
将函数对象作为返回值,函数也是对象。
- //first-class function
- return function(){
- //do sth
- }
4、命名式函数表达式(不常用)
同样是赋值给一个变量,但这个函数不是匿名函数,而是有一个名字的函数,
- //NFE (Named Function Expression)
- var add=function(a,b){
- //do sth
- }
3、函数声明和函数表达式的区别
最主要的区别是函数声明会被前置。
函数声明,在声明前调用也可以,因为会前置。
函数表达式在声明前调用会报错:undefined is not a function。
函数表达式中
var add=function(a,b){//do sth};
变量的声明会被提前,var add会提前,add被提前后它的值是undefined。
当把一个undefined的变量尝试像函数那样去调用的时候,就会报异常:undefined is not a function。
注意:在《jquery按钮绑定特殊事件》
中第一次点击按钮触发一个事件,第二次点击触发另一个事件。遇到类似的情况,
- //不要这样做
- if(condition){
- function sayHi(){
- alert("Hi!");
- }
- }else{
- function sayHi(){
- alert("Yo!");
- }
- }
- //可以这样做
- var sayHi;
- if(condition){
- sayHi=function(){
- alert("Hi!");
- }
- }else{
- sayHi=function(){
- alert("Yo!");
- }
- }
4、命名函数表达式(NFE)
函数名字可以被调试器和开发工具识别。
经典bug
命名函数表达式里的名字nfe在 函数对象创建所在的作用域中 正常情况下是访问不到的。所以会报错:nfe is not defined
老的IE浏览器(IE6~8)仍然可以访问得到,但是nfe和func又不是同一个对象。
命名函数表达式相关规范:
规范规定:命名函数表达式名字(标识符)在函数体的作用域内有效,且不能被覆盖。
- b = function c() {
- a = 1, b = 2, c = 3;
- console.log(typeof c); // function
- };
命名函数表达式应用:
调试
递归调用自己
- //递归调用
- var func=function nfe(){/** do sth. **/ nfe();}
5、函数构造器
除了函数声明和函数表达式,还有一种不常见的构造函数的方式—使用函数构造器。
Function()中参数可以有多个,前面的参数表示函数对象里面的形参,最后一个参数表示函数体里面的代码。
函数体里的代码也是字符串,这也是为什么函数构造器不常用的原因。
- var func=new Function('a','b','console.log(a+b);');
- //创建一个对象,有a,b2个形参,函数体里面是输出a+b
- func(1,2);//
- //不管用new还是不用new最后得到的结果都是一样的。
- var func=Function('a','b','console.log(a+b);');
- func(1,2);//
Function构造器作用域和其他处理与一般函数声明和函数表达式的差异
1、在Function构造器里面创建的变量仍然是局部变量,
- //CASE1
- Function('var localVal="local";console.log(localVal);')();//立即执行
- console.log(typeof localVal); //undefined
2、Function函数声明能访问到全局变量,却访问不到它的外函数中定义的变量。
local不可访问,全局global可以访问。
三、this
1、全局的this(浏览器)
全局作用域下的this一般指向全局对象,浏览器汇总的全局对象就是window。
2、一般函数的this(浏览器)【重点】
全局作用域下直接调用f1(),this就仍然指向全局对象,浏览器中就是window,在node.js里面就是global对象。
严格模式下直接调用f2(),this执行是undefined。
注意:语言设计上的不足【update 20170306】
调用函数时,this被绑定到全局对象。如下内部函数调用时this也是绑定到全局对象window。
- function outer(){
- console.log(this===window);
- function inner(){
- console.log("内部函数:"+(this===window));
- }
- inner();
- }
倘若语言设计正确,当内部函数被调用时,this应该仍然绑定到外部函数的this变量。这个设计错误的后果就是方法不能利用内部函数来帮助它工作,因为内部函数的this被绑定了错误的值,所以不能共享该方法对对象的访问权。
一个简单的解决方案如下:方法定义一个一个变量that并给它赋值为this,那么内部函数就可以通过that访问到this。
- <script>
- //函数字面量创建add
- var add=function(a,b){
- return a+b;
- }
- var myObject = {
- value: 0,
- increment: function(inc) {
- this.value += typeof inc === 'number' ? inc : 1;
- }
- }
- myObject.increment();
- document.writeln(myObject.value); //
- myObject.increment(2);
- document.writeln(myObject.value); //
- //给myObject增加一个double方法
- myObject.double=function(){
- var that=this;//解决方法
- var helper=function(){
- that.value=add(that.value,that.value);
- }
- helper(); //以函数的形式调用helper
- }
- //以方法的形式调用double
- myObject.double();
- document.writeln(myObject.value); //
- </script>
3、作为对象方法的 函数的this【重点】
只要将函数作为对象的方法o.f,this就会指向这个对象o。
- var o={
- prop:37,
- f:function(){
- return this.prop;
- }
- }
- console.log(o.f()); //
或者
- var o={prop:37};
- function independent(){
- return this.prop;
- }
- o.f=independent;
- console.log(o.f()); //
在方法调用模式中this到对象的绑定发生在调用时,这个“超级”延迟绑定(very late binding)使得函数可以对this高度重用。【update20170306】
4、对象原型链上的this
对象o有个属性f。
p是个空对象,并且p的原型指向o。给p添加2个属性a和b,再调用p.f()。
调用p.f()的时候调用的是p的原型o上面的这样一个属性f。所以对象原型链上的this调用时指向的还是对象。
- var o={f:function(){return this.a+this.b}};
- var p=Object.create(o);
- p.a=1;
- p.b=4;
- console.log(p.f()); //
5、get/set方法与this
get set方法中的this一般也是指向get,set方法所在的对象。
- function modulus(){
- return Math.sqrt(this.re*this.re+this.im*this.im);
- }
- var o={
- re:1,
- im:-1,
- get phase(){
- return Math.atan2(this.im,this.re);
- }
- }
- Object.defineProperty(o,'modulus',{
- get:modulus,
- enumerable:true,
- configurable:true
- })
- console.log(o.phase,o.modulus); //-0.7853981633974483 1.4142135623730951
6、构造器中的this【重点】
如果在一个函数前面带上new来调用,那么背地里将会创建一个连接到该函数的prototype成员的新对象,同时this会绑定到那个新对象上。
将MyClass作为了构造器来用。
- function MyClass(){
- this.a=37;
- }
- var o=new MyClass();
- /*this指向空对象,并且这个空对象的原型指向MyClass.prototype,
- this作为返回值,因为没有return
- 所以对象o就会有属性a为37*/
- console.log(o.a);//
注意:
return语句返回的是对象的话,将该对象作为返回值,所以下面a就是38。
- function C2(){
- this.a=37;
- return {a:38};
- }
- o=new C2();
- console.log(o.a);//
7、call/apply方法与this【重点】
- function add(c,d){
- console.log(this.a+this.b+c+d);
- }
- var o={a:1,b:3};
- //call调用
- add.call(o,5,7);//16 //1+3+5+7=16
- //apply调用
- add.apply(o,[10,20]);//34 //1+3+10+20=34
应用
- function bar(){
- console.log(Object.prototype.toString.call(this));
- }
- bar.call(7); //[object Number]
call和apply如果this传入null或者undefined的话,this会指向全局对象,在浏览器里就是window。
如果是严格模式的话:
传入this为null和undefined,那this就是null和undefined。
8、bind与this[ES5提供,IE9+才有]
想把某一个对象作为this的时候,就传进去。
- function f(){
- return this.a;
- }
- var g=f.bind({a:"test"});
- console.log(g());//test
- /*
- 绑定一次,多次调用,仍然实现这样一个绑定,比apply和call更高效
- */
- var o={a:37,f:f,g:g};
- /*f属性赋值为直接的f方法,g赋值为刚才绑定之后的方法*/
- console.log(o.f(),o.g()); //37 "test"
- /*o.f()通过对象的属性的方式调用的,返回37*/
- /*比较特殊的一点,使用bind方法绑定了之后,即使把新绑定之后的方法作为对象的属性去调用,仍然会按照之前的绑定去走,所以仍然返回test*/
应用【bind的经典例子】
- this.x=9; //相当于window.x=9
- var module={
- x:81,
- getX:function(){return this.x;}
- };
- module.getX();//81 作为对象方法调用
- var getX=module.getX();//把对象的方法赋值给一个变量
- getX();//9 this指向window,调用的是window的x
- var boundGetx=getX.bind(module);
- boundGetx();//81 通过bind修改运行时的this
四、函数属性arguments
foo.length拿到形参的个数。在函数内和函数外都有效。foo.length===arguments.callee.length【20170501】
arguments.length拿到实际传参的个数。
arguments.callee当前正在执行的函数
foo.name拿到函数名。
坑:尝试通过arguments[2]=100修改未传入的z的值,z还是undefined。
就是说:参数如果没传进来的话,arguments和参数没有改下修改这样的绑定关系。
- function foo(x,y,z){
- console.log(arguments.length); //
- console.log(arguments[0]); //
- arguments[0]=10;
- console.log(x); //有绑定关系,形参x被修改为10
- arguments[2]=100;//z未传入
- console.log(z);//没有绑定关系,z仍然是undefined
- console.log(arguments.callee===foo);//true,严格模式禁止使用
- }
- foo(1,2);
- console.log(foo.length);//
- console.log(foo.name);//"foo"
好处:使得编写一个无须指定参数个数的函数成为可能。
注意设计错误
因为语言设计错误,arguments并不是一个真正的数组。它只是一个"类似数组(array-like)"的对象。arguments拥有一个length属性,但arguments没有任何数组的方法。
五、bind和函数柯里化
函数柯里化就是把一个函数拆成多个单元。
1、柯里化
- function add(a,b,c) {
- console.log(a+'|'+b+'|'+c);
- console.log(a+b+c);
- }
//不需要改变this,所以传入一个undefined就可以了- var func=add.bind(undefined,100);
- func(1,2);
- //100|1|2
- //
- var func2=func.bind(undefined,200);
- func2(10);
- //100|200|10
- //
100固定赋值给a参数。
再柯里化一次,200固定赋值给b参数。
2、实际例子
- /*getConfig获取一套配置
- 在不同的页面中配置可能是不一样的,
- 但是在同一个模块下,可能前2个参数,或者某些参数是一样的
- */
- function getConfig(colors,size,otherOptions){
- console.log(colors,size,otherOptions);
- }
- /*this无所谓,写个null或者undefined都可以,可能在某个模块下color都是#CC0000,size都是1024*768*/
- var defaultConfig=getConfig.bind(null,"#CC0000","1024*768");
- /*拿到defaultConfig这样一个模块级别的通用配置以后,只要传入最后一个参数,可能是每个页面下的单独配置*/
- defaultConfig("123"); //#CC0000 1024*768 123
- defaultConfig("456"); //#CC0000 1024*768 456
六、bind和new
用new去调用,在this这个层面上.bind()的作用会被忽略。
用new的时候,即使绑定了bind,也会被忽略。
func()直接调用,this会指向bind参数{a:1},return this.a就会返回1.
执行了this.b=100其实是给{a:1}加了个b属性,最后是{a: 1, b: 100}只是不会作为返回值,因为指定了返回值。
new调用的话,return除非是对象,不是对象的话会把this作为返回值,并且this会被初始化为默认的一个空对象,这个对象的原型是foo.prototye。
所以这里new func()调用的时候,即使我们指定了bind方法,this仍然会指向没有bind时所指向的空对象,空对象的原型指向foo.prototype,这个空对象的b属性被设置为100,整个对象会作为一个返回值返回,会忽略return this.a。所以用new func()调用后会返回对象字面量{b:100}。
七、bind方法模拟
在老的浏览器里怎样实现bind方法?模拟实现。
bind方法实现2个功能,绑定this和柯里化。
MDN的模拟实现。
本文作者starof,因知识本身在变化,作者也在不断学习成长,文章内容也不定时更新,为避免误导读者,方便追根溯源,请诸位转载注明出处:http://www.cnblogs.com/starof/p/6396450.html有问题欢迎与我讨论,共同进步。
javascript 函数和作用域(函数,this)(六)的更多相关文章
- python学习第三十一天函数的嵌套及函数的作用域
python函数的嵌套是指在函数里面嵌套另外一个函数,可以嵌套更多,函数一旦套用了另外一个函数,他的作用域就已经形成,可以通过global关键词改变变量的作用域,下面详细说明函数的嵌套及函数的作用域 ...
- JavaScript函数及作用域
知识内容: 1.JavaScript函数 2.JavaScript全局函数及特殊函数 3.JavaScript作用域 4.本节练习 参考资料:<JavaScript高级程序设计> 一.Ja ...
- JavaScript基础学习-函数及作用域
函数和作用域是JavaScript的重要组成部分,我们在使用JavaScript编写程序的过程中经常要用到这两部分内容,作为初学者,我经常有困惑,借助写此博文来巩固下之前学习的内容. (一)JavaS ...
- 深入理解javascript函数定义与函数作用域
最近在学习javascript的函数,函数是javascript的一等对象,想要学好javascript,就必须深刻理解函数.本人把思路整理成文章,一是为了加深自己函数的理解,二是给读者提供学习的途径 ...
- JavaScript变量的作用域和函数的作用域的区别
变量作用域和函数作用域都涉及到变量值的变化,本文旨在让大家明白他们之间的区别 变量的作用域: 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接 ...
- javascript 函数及作用域总结介绍
在js中使用函数注意三点: 1.函数被调用时,它是运行在他被声明时的语法环境中的: 2.函数自己无法运行,它总是被对象调用的,函数运行时,函数体内的this指针指向调用该函数的对象,如果调用函数时没有 ...
- [译]JavaScript:函数的作用域链
原文:http://blogs.msdn.com/b/jscript/archive/2007/07/26/scope-chain-of-jscript-functions.aspx 在JavaScr ...
- JavaScript中的作用域与函数和变量声明的提升
var foo = 1; function bar() { if (!foo) { var foo = 10; } alert(foo); } bar(); ...
- 我所理解的javascript中函数的作用域和作用域链
本文为原创,转载请注明出处: cnzt 文章:cnzt-p 写在前面 一周木有更新了,今天终于攻克了自行车难关,非常开心,特意来一更~ (那些捂嘴偷笑的人我看到你们了快把嘴闭上我会假装没看 ...
随机推荐
- js正则表达式详解及示例讲解
所谓正则表达式,简单来说就是一种规则,一种计算机能读懂的规则.js中的正则表达式语法是Perl5(一种很早的编程语言)的正则语法的子集.本文将在基础知识的基础上添加示例帮助快速理解正则表达式. 学习正 ...
- Swift应用案例 2.闭包入门到精通
本文主要介绍Swift的闭包的使用并与OC的Block做比较.学习Swift是绕不过闭包的,因为无论是全局函数还是嵌套函数都是闭包的一种,本文主要介绍闭包表达式. 1.闭包表达式的使用 // 1. ...
- RunTime 给类添加属性
RunTime网上有很多人都不知道Runtime到底是干嘛的?有很多博主都是长篇大论给他们讲这个讲那个,我感觉还不如实例来的实在.很简单的一个例子:我们都知道会有这样的需求,未读消息列表的图片上要有一 ...
- PuTsangTo-单撸游戏开发04 给角色添加基本动画
一. 跳跃与移动的优化与完善 先给上一次的内容做一次补救,也就是上一次中还留存的,由于键盘按键事件的第一次回调与后续回调之间会间隔个小半秒带来的跳跃落地后动作延迟的情况. 最终的键盘按下回调的处理代码 ...
- 自动生成并导出word文档
今天很荣幸又破解一现实难题:自动生成并导出word文档 先看页面效果: word效果: 代码: 先搭建struts2项目 创建action,并在struts.xml完成注册 <?xml vers ...
- 3.Java日志框架slf4j、jcl、jul、log4j1、log4j2、logback大总结
一.slf4j.jcl.jul.log4j1.log4j2.logback JUL:JDK中的日志记录工具,也常称为JDKLog.jdk-logging. LOG4J1:一个具体的日志实现框架. LO ...
- Android™ 1.5 android.R.drawable Icon Resources
图标一览表: http://www.darshancomputing.com/android/1.5-drawables.html 官 方 API: http://developer.androi ...
- Android完全退出activity
在Android中,如果想退出Android程序,一般都是调用finish().System.exit(0).android.os.Process.killProcess(android.os.Pro ...
- Git安装与上传代码至Github
转载请注明出处:http://www.cnblogs.com/cnwutianhao/p/6642887.html 这篇文章应该是全网最新,最全,最靠谱的Github安装到上传代码的流程. 1.Git ...
- nodejs 后台服务启动
最近一个项目微信小程序,需要写个小型的后端程序处理聊天通讯记录保存,主要是功能是组建群聊天室,所以用node写了个websocket服务... 但是终端连接到服务器,运行 node server.js ...