红皮书第七章学习心得
提醒:本文发布于 3556 天前,文章内容可能 因技术时效性过期 或 被重新修改,请谨慎参考。
变量
变量类型 , 当一个值赋给变量时, 解析器必须确定这个值是基本类型还是引用类型
- 基本类型: 简单的数据段,按值访问: 有Undefined / null / boolean / number / string
- 引用类型: 多个值构成的对象 , 不能直接访问对象的内存空间. 实际操作的是它的引用(指针)
区别
这两个类型的区别:
属性: 只有引用类型才能添加属性 , 基本类型无法添加.
复制:
- 基本类型: 复制一个数据副本. 新创建的变量完全是独立的.
- 引用类型: 只是新建一个引用(指针) , 两个变量依然指向同一个内存空间.
传递参数: 参数都是按值传递,且只能按值传递
基本类型
这个好理解,略
引用类型:
关键: 按值传递(非常重要)
例子1:
function setName(obj){ |
例子2:
function setName(obj){ |
例子3:
function setName(obj){ |
检测方式
- typeof (基本类型检测方式)
- instanceof (引用类型检测方式)
注意事项
仅仅用typeof
或者instanceof
来检测引用类型都可能是不靠谱的,为什么不靠谱呢?
以下解释引自红皮书第22章:
Safari(直至第 4 版)在对正则表达式应用typeof操作符时会返回”function”,因此很难确定某个值到底是不是函数。
instanceof操作符在存在多个全局作用域(像一个页面包含多个frame)的情况下,也是问题多多。
如:
var isArray = value instanceof Array; |
以上代码要返回true,value必须是一个数组,而且还必须与Array构造函数在同个全局作用域中。(别忘了, Array 是 window 的属性。)如果 value 是在另个 frame 中定义的数组,那么以上代码就会返回 false。
所以解决办法是什么呢?
Object.prototype.toString.call(value) |
用这来检测引用类型才是最稳妥的
执行环境和作用域
全局环境就是window对象所以的全局变量和函数都是作为window对象的属性和方法创建的.(执行幻境中所有的代码执行完之后,环境就会被销毁,环境内的变量和函数定义也会被销毁)每个函数都有自己的执行环境, 函数在执行时, 函数的环境就会被推入一个环境栈中,然后创建变量对象的 作用域链 , 其作用是保证执行环境对变量和函数的有序访问.执行完之后再弹出.
在函数中,作用域链的最前端是arguments对象,然后逐级向外读取变量,直到全局,全局环境的变量对象始终是作用域链的最后一个对象.
函数声明方式
通常使用以下两种方式:
函数声明
function fname(arg0, arg1, arg2 ){ |
函数声明的重要特征是: 函数声明提升, 在执行代码之前会先读取函数声明. 即便把函数声明放在执行代码的后面也如此.
函数表达式(匿名函数)
var fname=function(arg0, arg1, arg2 ){ |
匿名函数常常当成值来使用. 但是不是它的唯一作用.
递归
这是常见的递归函数
function recursive(i){ |
再看一个例子
function factorial(num){ |
所以函数名只是一个地址
闭包和匿名函数
闭包, 是有权访问另一个函数作用域中的变量的函数(注意闭包是函数).常见的闭包创建方式, 在一个函数的内部创建另一个函数.
当函数第一次被调用时,会创建一个执行环境以及相应的作用域链并把作用域链赋给一个特殊的内部属性[[scope]],然后使用this arguments和其他参数来初始化活动对象(即变量),但在作用域链中外部函数的活动对象逐级增加.
例如:
function compare(v1, v2){ |
compare内会创建this , arguments ,v1 , v2 的活动对象.全局执行环节的变量对象 this result compare处于第二位.
作用域链本质上是一个执行变量对象的指针列表,它只引用但不实际包含变量对象.一般来说,函数执行完之后, 局部活动对象就会被销毁, 内存中只保留全局作用域.
但是闭包不一样.
比如说,下面这个例子,我们一看以为值是My Object,但是并非如此.
var name = "The Window"; |
如果要达到期待的效果:
var name = "The Window"; |
闭包与变量
典型的闭包:
function f1(){ |
作用域链这种配置机制出现一个副作用,闭包只能取得包含函数中任何变量的最后一个值. 因为闭包(是一个函数), 保存的是整个对象,而不是某个特殊的对象.
例子1:
function cf(){ |
例子2:
function cf(){ |
例子3:
function cf(){ |
This对象
this对象是基于函数执行环境绑定的, 全局环境中, this==window, 函数为某个对象的方法时且被调用时, this为当前对象.
但是匿名函数的指向环境具有全局性 ,通常指向window. 由于闭包的写法不一样, 可能不太明显
var name = "The Window"; |
闭包与内存泄露
以下代码容易发生无意识内存泄露:
function fn(){ |
这段代码获取一个DOM元素并为其设置字体颜色,但它已经发生了内存泄露,为什么?因为el
的引用放在了匿名函数中.这在函数内部和本地对象(el)创建了一个循环引用.
改进方法:
function fn(){ |
访客评论