位置: 编程技术 - 正文

JavaScript高级程序设计(第3版)学习笔记8 js函数(中)(javascript高级程序设计最新版)

编辑:rootadmin
6、执行环境和作用域 (1)执行环境(execution context):所有的JavaScript代码都运行在一个执行环境中,当控制权转移至JavaScript的可执行代码时,就进入了一个执行环境。活动的执行环境从逻辑上形成了一个栈,全局执行环境永远是这个栈的栈底元素,栈顶元素就是当前正在运行的执行环境。每一个函数都有自己的执行环境,当执行流进入一个函数时,会将这个函数的执行环境压入栈顶,函数执行完之后再将这个执行环境弹出,控制权返回给之前的执行环境。 (2)变量对象(variable object):每一个执行环境都有一个与之对应的变量对象,执行环境中定义的所有变量和函数就是保存在这个变量对象中。这个变量对象是后台实现中的一个对象,我们无法在代码中访问,但是这有助于我们理解执行环境和作用域相关概念。 (3)作用域链(scope chain):当代码在一个执行环境中运行时,会创建由变量对象组成的一个作用域链。这个链的前端,就是当前代码所在环境的变量对象,链的最末端,就是全局环境的变量对象。在一个执行环境中解析标识符时,会在当前执行环境相应的变量对象中搜索,找到就返回,没有找到就沿着作用域链一级一级往上搜索直至全局环境的变量对象,如果一直未找到,就抛出引用异常。 (4)活动对象(activation object):如果一个执行环境是函数执行环境,也将变量对象称为活动对象。活动对象在最开始只包含一个变量,即arguments对象(这个对象在全局环境的变量对象中不存在)。   这四个概念虽然有些抽象,但还是比较自然的,可以结合《JavaScript高级程序设计(第3版)》中的一个例子来细细体会一下: 这里的整个过程是: (1)进入全局环境,创建全局变量对象,将全局环境压入栈顶(这里也是栈底)。根据前面的关于声明提升的结论,这里创建全局变量对象可能的一个过程是,先创建全局变量对象,然后处理函数声明设置属性changeColor为相应函数,再处理变量声明设置属性color为undefined。 (2)执行全局环境中的代码。先执行color变量初始化,赋值为'blue',再调用changeColor()函数。 (3)调用changeColor()函数,进入到changeColor函数执行环境,创建这个环境相应的变量对象(也就是活动对象),将这个环境压入栈顶。创建活动对象可能的一个过程是,先创建活动对象,处理内部函数声明设置属性swapColors为相应函数,处理函数参数创建活动对象的属性arguments对象,处理内部变量声明设置属性anotherColor为undefined。 (4)执行changeColor()函数代码。先执行anotherColor初始化为'red',再调用swapColors()函数。 (5)调用swapColors()函数,进入到swapColors函数执行环境,创建相应的变量对象(活动对象),将swapColors执行环境压入栈顶。这里创建活动对象可能的一个过程是,先创建活动对象,处理函数参数,将形式参数作为活动对象的属性并赋值为undefined,创建活动对象的属性arguments对象,并根据实际参数初始化形式参数和arguments对应的值和属性(将属性color1和arguments[0]初始化为'white',由于没有第二个实际参数,所以color2的值为undefined,而arguments的长度只为1了),处理完函数参数之后,再处理函数内部变量声明,将tempColor作为活动对象的属性并赋值为undefined。 (6)执行swapColors()函数代码。先给tempColor初始化赋值,然后实现值交换功能(这里color和anotherColor的值都是沿着作用域链才读取到的)。 (7)swapColors()函数代码执行完之后,返回undefined,将相应的执行环境弹出栈并销毁(注意,这里会销毁执行环境,但是执行环境相应的活动对象并不一定会被销毁),当前执行环境恢复成changeColor()函数的执行环境。随着swapColor()函数执行完并返回,changeColor()也就执行完了,同样返回undefined,并将changeColor()函数的执行环境弹出栈并销毁,当前执行环境恢复成全局环境。整个处理过程结束,全局环境直至页面退出再销毁。   作用域链也解释了为什么函数可以在内部递归调用自身:函数名是函数定义所在执行环境相应变量对象的一个属性,然后在函数内部执行环境中,就可以沿着作用域链向外上溯一层访问函数名指向的函数对象了。如果在函数内部将函数名指向了一个新函数,递归调用时就会不正确了: 关于作用域和声明提升,再看一个例子: 这里最不直观的可能是第3行输出undefined,因为在全局中已经定义过name了,不过按照上面解析的步骤去解析一次,就可以得出正确的结果了。另外强调一下,在ECMAScript中只有全局执行环境和函数执行环境,相应的也只有全局作用域和函数作用域,没有块作用域——虽然有块语句。 对于作用域链,还可以使用with、try-catch语句的catch块来延长: &#;使用with(obj){}语句时,将obj对象添加到当前作用域链的最前端。 &#;使用try{}catch(error){}语句时,将error对象添加到当前作用域链的最前端。   插了一段较为抽象的概念,希望不至于影响整个阅读的流畅,事实上,我在这里还悄悄的绕过了一个称为“闭包”的概念,关于函数与闭包,在下篇文章中再详细叙述。 7、函数内部对象与this   对于面向对象语言的使用者来说,this实在是再熟悉不过了,不就是指向构造函数新创建的对象吗!不过,在ECMAScript中,且别掉以轻心,事情没有那么简单,虽然在使用new操作符调用函数的情况下,this也的确是指向新创建的对象,但这只是指定this对象值的一种方式而已,还有更多的方式可以指定this对象的值,换句话说,this是动态的,是可以由我们自己自由指定的。 (1)全局环境中的this   在全局环境中,this指向全局对象本身,在浏览器中也就是window,这里也可以把全局环境中的this理解为全局执行环境相应的变量对象,在全局环境中定义的变量和函数都是这个变量对象的属性: 如果在自定义函数中要引用全局对象,虽然可以直接使用window,但更好的方式则是将全局对象作为参数传入函数,这是在JS库中非常通用的一种方式: 这种方式兼容性更好(ECMAScript的实现中全局对象未必都是window),在压缩时,也可以将global简化为g,而不用使用window了。 (2)函数内部属性this   在函数环境中,this是一个内部属性对象,可以理解成函数对应的活动对象的一个属性,而这个内部属性的值是动态的。那this值是怎么动态确定的呢? &#;使用new调用时,函数也称为构造函数,这个时候函数内部的this被指定为新创建的对象。 需要注意区分一下函数执行环境中定义的属性(也即活动对象的属性)和this对象的属性,在使用数组元素方式调用函数时,函数内部this指向数组本身,因此上例最后输出undefined。 &#;作为一般函数调用时,this指向全局对象。 &#;作为对象的方法调用时,this指向调用这个方法的对象。   看下面的例子: 这里函数对象本身是匿名的,是作为person对象的一个属性,当作为对象属性调用时,this指向了对象,当把这个函数赋给另一个函数然后调用时,是作为一般函数调用的,this指向了全局对象。这个例子充分说明了“函数作为对象的方法调用时内部属性this指向这个调用对象,函数作为一般函数调用时内部属性this指向全局对象”,也说明了this的指定是动态的,是在调用时指定的,而不管函数是单独定义的还是作为对象方法定义的。也正是因为函数作为对象的方法调用时this指向这个调用对象,所以在函数内部返回this时才能够延续调用对象的下一个方法——也就是链式操作(jQuery的一大特色)。 &#;使用apply()、call()或bind()调用函数时,this指向第一个参数对象。如果没有传入参数或传入的是null和undefined,this指向全局对象(在ES5的严格模式下会设为null)。如果传入的第一个参数是一个简单类型,会将this设置为相应的简单类型包装对象。 上面示例中列出的都是一些常见情况,没有列出第一个参数为null或undefined的情况,有兴趣的朋友可以自行测试。关于this值的确定,在原书中还有一个例子: 第1个是正常输出,第2个(object.getName)与object.getName的效果是相同的,而第3个(object.getName=object.getName)最终返回的是函数对象本身,也就是说第3个会作为一般函数来调用,第4个则先是调用getNameFunc这个方法,返回一个函数,然后再调用这个函数,也是作为一般函数来调用。 8、函数属性和方法   函数是一个对象,因此也可以有自己的属性和方法。不过函数属性和方法与函数内部属性很容易混淆,既然容易混淆,就把它们放一起对照着看,就好比一对双胞胎,不对照着看,不熟悉的人是区分不了的。   先从概念上来区分一下: (1)函数内部属性:可以理解为函数相应的活动对象的属性,是只能从函数体内部访问的属性,函数每一次被调用,都会被重新指定,具有动态性。 (2)函数属性和方法:这是函数作为对象所具有的特性,只要函数一定义,函数对象就被创建,相应的属性和方法就可以访问,并且除非你在代码中明确赋为另一个值,否则它们的值不会改变,因而具有静态性。有一个例外属性caller,表示调用当前函数的函数,也是在函数被调用时动态指定,在《JavaScript高级程序设计(第3版)》中也因此将caller属性和函数内部属性arguments、this一起讲解,事实上,在ES5的严格模式下,不能对具有动态特性的函数属性caller赋值。   光从概念上区分是非常抽象的,也不是那么容易理解,再把这些属性列在一起比较一下(没有列入一些非标准的属性,如name): 类别名称继承性说明备注函数内部属性this-函数据以执行的环境对象和一般面向对象语言有很大区别arguments-

表示函数实际参数的类数组对象

arguments本身也有自己的属性:length、callee和caller

1、length属性表示实际接收到的参数个数

2、callee属性指向函数对象本身,即有:

  fn.arguments.callee === fn

3、caller属性主要和函数的caller相区分,值永远都是undefined

函数属性caller否调用当前函数的函数虽然函数一定义就可访问,但是不在函数体内访问时永远为null,在函数体内访问时返回调用当前函数的函数,在全局作用域中调用函数也会返回nulllength否函数形式参数的长度就是定义函数时命名的参数个数prototype否函数原型对象原型对象是ECMAScript实现继承的基础constructor是继承自Object,表示创建函数实例的函数,也就是Function()值永远是Function,也就是内置的函数Function()函数方法apply否调用函数自身,以(类)数组方式接受参数

这三个方法主要作用是动态绑定函数内部属性this

1、apply和call在绑定之后会马上执行

2、bind在绑定之后可以在需要的时候再调用执行

call否调用函数自身,以列举方式接受参数bind否绑定函数作用域,ES5中新增toLocalString覆盖

覆盖了Object类型中的方法,返回函数体

不同浏览器实现返回可能不同,可能返回原始代码,也可能返回去掉注释后的代码

toString覆盖valueOf覆盖hasOwnProperty是直接继承自Object类型的方法,用法同ObjectpropertyIsEnumerable是isPropertyOf是

  函数属性和方法,除了从Object继承而来的属性和方法,也包括函数本身特有的属性和方法,用的最多的方法自然就是上一小节说的apply()、call(),这两个方法都是用来设置函数内部属性this从而扩展函数作用域的,只不过apply()扩展函数作用域时是以(类)数组方式接受函数的参数,而call()扩展函数作用域时需要将函数参数一一列举出来传递,看下面的例子:

不过需要强调的是:apply和call的主要作用还是在于扩展函数作用域。apply和call在扩展作用域时会马上调用函数,这使得应用中有了很大限制,因此在ES5中新增加了一个bind()函数,这个函数也用于扩展作用域,但是可以不用马上执行函数,它返回一个函数实例,将传入给它的第一个参数作为原函数的作用域。它的一个可能的实现如下: 这里涉及了一个闭包的概念,明天再继续。

推荐整理分享JavaScript高级程序设计(第3版)学习笔记8 js函数(中)(javascript高级程序设计最新版),希望有所帮助,仅作参考,欢迎阅读内容。

JavaScript高级程序设计(第3版)学习笔记8 js函数(中)(javascript高级程序设计最新版)

文章相关热门搜索词:javascript高级程序设计第五版 pdf下载,javascript高级程序设计电子版,javascript高级程序设计第五版 pdf下载,javascript高级程序设计pdf百度云,javascript高级程序设计pdf百度云,javascript高级程序设计第六版,javascript高级程序设计电子版,javascript高级程序设计电子版,内容如对您有帮助,希望把文章链接给更多的朋友!

JavaScript高级程序设计(第3版)学习笔记9 js函数(下) 再接着看函数——具有魔幻色彩的对象。9、作为值的函数在一般的编程语言中,如果要将函数作为值来使用,需要使用类似函数指针或者代理的方式来

JavaScript高级程序设计(第3版)学习笔记 再访js对象 1、对象再认识(1)对象属性和特性什么是属性(Property),什么是特性(Attribute),这有什么区别?我不想也不会从语义学上去区分,对于这系列文章

JavaScript高级程序设计(第3版)学习笔记 内建js对象 内建对象就好比是JDK中的类库,开发者可以直接拿来使用,这极大的方便了常见的编程任务。这篇文章就来浏览一下主要的内建对象,当然,我们并不

标签: javascript高级程序设计最新版

本文链接地址:https://www.jiuchutong.com/biancheng/380512.html 转载请保留说明!

上一篇:JavaScript高级程序设计(第3版)学习笔记7 js函数(上)(javascript高级程序设计pdf百度云)

下一篇:JavaScript高级程序设计(第3版)学习笔记9 js函数(下)(javascript高级程序设计第五版 pdf下载)

  • 小规模增值税附表一怎么填
  • 企业间无偿划转资产,非关联方经营
  • 不征税收入有哪些类型
  • 房屋租赁发票票样
  • 主营业务收入为什么记贷方
  • 装载机折旧年限是几年
  • 收到供应商赠送的发票
  • 住房贷款利息专项附加扣除是返钱吗
  • 印花税少提了怎么办
  • 公司之间借钱不还违法吗
  • 报关单不存在
  • 购买未完工的厂房会计分录
  • 专用发票报销时限
  • 个人独资企业核定
  • 一般户开户行可以开增值税专票么
  • 开具红字专票的步骤
  • 出租房产业务企业所得税如何处理?
  • 转让地下建筑物交土地增值税吗
  • 制造费用和劳务成本
  • 技术合同 免税
  • 免税农产品加工企业
  • 小米路由器启动不了
  • 月末累计折旧怎么结转
  • 企业财政拨款所得免税吗
  • 小规模计提增值税的会计科目
  • 餐费发票可以抵扣成本吗
  • le启动exe是什么意思
  • linux 引导
  • 与取得收入无关的其他支出
  • php正则替换字符串
  • 巴伐利亚知乎
  • 合并报表内部交易顺流逆流
  • 房租违约金怎么开发票
  • yii gridview
  • 【深度学习时间序列预测案例】零基础入门经典深度学习时间序列预测项目实战(附代码+数据集+原理介绍)
  • php die exit
  • 不含税销售额和计税销售额
  • 金融机构拆出资金的最长期限
  • 什么是银行对账,进行银行对账分为几步
  • 取得增值税
  • 拆迁支出包括哪些
  • mysql中事务的作用
  • SQLite3 API 编程手册
  • 一般项目的进项税和即征即退进项税
  • 合并报表抵消分录
  • 故障的机器修好多少钱
  • 企业非正常损失如何处理
  • 电子承兑到期怎么收款
  • 不需要支付的应付账款
  • 存货周转率多少算合理
  • 印花税 不足
  • 企业租入设备的会计分录怎么写
  • 机器设备进项税率
  • 小规模升级一般纳税人有什么好处
  • 饭店的会计做账怎么做
  • 银行对账单怎么修改
  • 零售商品购进的核算需要设置什么科目
  • linux系统关机重启命令
  • solaris 2020
  • windowsserver2008r2忘记开机密码怎么办
  • win8系统一直重启
  • Win10 Mobile 10572怎么更新升级? 需回滚到WP 8.1
  • 一键ghostcgi
  • Win10预览版桌面图标和任务栏不翼而飞怎么办?
  • SsAAD.exe - SsAAD是什么进程
  • Mac通过AppleID登录窗口
  • 王牌竞速 小米
  • 联想电脑win7系统开机修改开机密码
  • win7旗舰版虚拟光驱下载
  • html5能做游戏吗
  • perl cgi
  • Android Bound Service(二) ----- Using AIDL
  • linux文件处理三剑客
  • python 异步操作
  • input输入@弹出框
  • java教程 视
  • javascript的dom操作
  • apk反编译去广告教程
  • 广西南宁税务12366
  • 广东省东莞市电子税务局官网
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设