什么是作用域, 什么事闭包, 什么事原型链
Posted lcf1314
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是作用域, 什么事闭包, 什么事原型链相关的知识,希望对你有一定的参考价值。
什么是作用域 |
作用域即作用范围,在js中采用的是词法作用域,所谓的词法作用域之的是在代码编写的过程中体现出来的作用范围,代码一旦写好,不用执行作用范围就已经决定了,这个就是词法作用域
在js中作用域的规则,
* 函数运行访问函数外的数据
* 在整个代码中只有函数可以限定作用域
* 首先需要考虑提升规则
* 如果当前作用域中已经有名字,就不再考虑函数外的名字(就近原则)
# 作用域链
1. 在js中只有函数可以制造作用域,只要有代码就会有一个作用域即全局作用域,凡是代码中有函数那么这个函数就构成了一个新的作用域,如何函数中还有函数,那么在这个作用域中就会再有一个新的作用域诞生,这样将所有的作用域列出来就构成了作用域链
# 变量的访问
* 首先看变量在第几条链上(即哪个作用域),在该链上看是否存在变量的定义与赋值,如果有直接拿来使用,
* 如果没有去上一层链去查找,如果有直接使用,停止继续查找
* 如果还没有就继续查找.直到找到全局作用域,如果全局作用有中没有,就是is not defined
* 同级链是不可以混合查找的
|
#代码预解析(变量提升) |
# 在程序执行的过程中,会先将代码读取内存中,会将所有的声明在此时进行标记,所谓的标记就是告诉解释器知道有这个名字,后面再使用名字的时候,不会出现未定义的错误,这个标记过程就是变量名提升
# 名字声明:
即变量名提升 :告诉解释器知道已 经有这个名字了。且只提升变量名(没有任何数据与其对应)
# 函数声明:函数声明包括两个部分:
1. 首先函数声明告诉解释器,这个名字已经存在,还阶段与变量名提升一样
2. 告诉解释器,这个名字对应的函数体是什么
* . 函数声明与函数表达有区别,函数声明单独写在一个结构中,不存在任何语句。逻辑判断等结构中
* . 函数后面不加括号
|
什么是闭包? |
1. 闭包的含义就是闭合,简单的说就是一个具有封闭风格与包裹功能的一个结构,所以闭包就是一个具有封闭对外不公开的包裹结构或空间
2. 在js中函数可以构成闭包,即一个函数是一个代码结构的封闭结构,即包裹的特性。根据作用域规则,只允许函数访问外部的数据,外部无法访问函数内部的数据,即封闭的对外不公开的特性,因此说函数可以构成闭包
举个例子:
var foo = (function(){
return {}
或者 return function(){}
})()
# 闭包要解决的问题
1. 闭包不允许外界访问
2. 要解决的问题就是直接或间接的范围内部数据
## 函数可以构成闭包,解决的问题就是外部可以访问函数内部的数据
1. 函数内部的数据直接return 是可以访问到函数内部的数据的,但是该数据不能第二次访问,
因为第二次访问的时候又要调用一个函数,表示会有一个新的数据出来
3. 在函数内部的数据,不能直接被函数外部访问,所以在函数内部定义一个函数,那么这个内部函数是可以直接被访问的(即访问闭包中的数据)
4. 0 级俩无法访问1级链中的数据,所以可以通过操作2级链来访问1级链的数据
# 闭包的基本结构
1. 一般闭包的问题就是要使用访问间接的获取函数内部数据的使用权:有2个基本使用模型
* 写一个函数,函数内部定义一个新函数,返回这个新函数,利用函数获取内部的数据
* 写一个函数,函数内部定义一个对象,对象中绑定多个函数,返回对象,利用对象的方法访问函数内部的数据
# 闭包的基本用法
1 . 闭包是为了实现具有私有访问空间的函数
2. 带有私有访问数据的对象
* 私有数据,就是值有函数内部可以访问的数据,或对象内部的方法可以访问的数据
3. 带有私有数据的函数
# 闭包的性能问题
1. 函数执行需要内存,那么函数定义的变量,会在函数执行结束后自动回收(即垃圾回收),凡是因为闭包被引出的数据,如果还有变量在引用,这些数据就不会被回收。,
2. 引出使用闭包的时候,如果不使用某一些数据了,一定要给他赋值一个NULL(IE浏览器一定要写)
3. 函数运行需要内存,函数运行结束,内存回收
4. 闭包中的数据被外界访问后,函数执行结束后不能将其回收,如果闭包过过,会造成
内存泄露(即闭包中的数据被访问后不会被回收,如果闭包过多,数据一致堆叠在内存中,会找出内存泄露)(所以有一些数据不在使用的时候,如果有变量还在引用这个数据,就把这个变量赋值为空)
|
# 什么是递归 |
1 . 在程序中,递归就是函数直接或间接的调用自己
2. 递归而言最重要的就是跳出结构,跳出了才会有结果(一般通过判断条件跳出)
# 所谓的递归就是化归思想
下面可以不答
(1. 递归的调用,写递归函数,最终还是包要转换为自己这个函数
2. 递归思想就是将一个问题转换为一个已解决的问题来实现)
|
# 什么是函数函数声明,什么是函数表达式 |
1. 函数声明function foo(){}
* 会提升 在函数声明之前也可以调用
* 不能写在逻辑块,语句,表达式中间
* 必须保证函数声明代码上面或下面不是一个逻辑体
* 可以声明函数的位置:全局作用域,函数中
2. 函数表达式 var foo = function(){}
* 使用运算符可以将函数转换成表达式(function(){})!function(){}在前面加运算符,所有非空对象,转换成boolean值都是真
* 放在if while fo do while ...的中间
* 表达式是一个赋值语句,所以foo作为变量名可以提升,但是函数不会提升,不能在函数前面调用,至于你说赋值后调用
|
#什么是原型(神秘对象就是函数.prototype) |
1. 原型能存储一些方法,构造函数创建的对象能够访问这些方法,原型能实现继承
2. 在创建一个函数时候,会同时创建一个特殊的神秘对象,该对象使用函数.prototype引用,称其为函数的原型属性
3. 创建出来的神秘对象针对于构造函数,称为构造函数的原型属性
4. 创建出来的神秘对象针对于构造函数创建出来的实例对象,称为构造函数的原型对象
5. 构造函数创建的实例对象直接‘含有‘神秘对象的方法,即原型继承
6. 实例对象在调用当前对象的属性和方法的时候,如果当前对象中没有这些属性和方法,那么就会到原型对象中去找
|
# __proto__ |
* 以前要访问原型,必须使用构造函数来实现,无法直接使用实例对象来访问原型
* 火狐最早引入属性‘__proto__‘表示使用实例对象引用原型,但是早期是非标准的
* 通过该属性可以允许使用实例对象直接访问原型
* 可以使用实例对象.__proto__也可以直接访问神秘对象
* 实例对象.__proto__ == 构造函数.prototype
|
# 什么对象的结构 |
1. 神秘对象中默认都有一个熟悉‘constructor‘,翻译为构造器,表示该原型是与什么构造函数联系起来的。
2. ‘__proto__‘有什么用?
* 可以访问原型
* 在开发中除非特殊要求,不要使用实例去修改原型的成员,因此该属性开发时使用较少
* 但是在调试的过程中会非常方便,可以轻易的访问原型进行查看成员
3. 在早期的浏览器中使用实例需要访问原型如何处理?
* 可以使用实例对象访问构造器,然后使用构造器访问原型
o.constructor.prototype
4. 如果给实例继承自原型的属性赋值
* 会给自己添加属性 不会改变原型中的属性
* 如果访问数据,当前对象中如果没有该数据就到构造函数的原型属性中去找
|
# 继承 |
1 . 最简单的继承就是 将别的对象的属性强加在当前对象的上面,那么当前对象就有这个成员了
2. 利用原型也可以实现继承,不需要在当前对象身上添加任何成员,只要原型有了,当前对象就有了
3. 将属性,方法等成员利用混入的方式,加到构造函数的原型上,那么构造函数的实例就具有属性,和方法了
|
##混合式继承赋值描述 |
##使用点语法给原型添加成员,与使用直接替换修改原型对象有什么区别?
1. 原型指向发生了变化
2. 构造函数所创建的对象所继承的原型不同
3. 新增的对象默认是没有constructor属性
注意: 在使用替换的方式修改原型的时候,一般会添加constructor属性
4. 构造函数在调用的时候,根据不同的参数创建不同的对象
* $()使用
* 可以放入字符串:转换成html字符串,也可以作为选择器
* dom:包装成jq对象
* jq对象:便于操作
5. 有时候根据需要在构造函数的内部还会调用构造函数
* 为了在构造函数内部通过constructor创建对象
6. 在这种情况下不要使用构造函数的名字,而要使用‘this.constructor‘
|
#属性搜索原则 |
1. 原型链
2. 属性搜索原则
* 所谓的属性搜索原则,就是对象在访问属性和方法的时候,首先在当前对象中查找
* 如果当前对象中存在该属性或方法,停止查找,直接使用该属性与方法
* 如果对象没有该成员,那么在其原型对象中查找
* 如果原型对象含有该成员,那么停止查找,直接使用
* 如果原型还没有,就到原型的原型中查找
* 如此往复,指导找到Object.prototype还没有 就返回undefined
* 如果是调用方法就报错,该方法不是一个函数
|
# 原型的概念 |
1. 关于面向对象的概念
* 类 class:在js中就是构造函数
* 在传统的面向对象语言中,使用一个叫做类的东西定义模板,然后使用模板创建对象
* 在构造方法中也具有类似的功能,因此称其为类
* 实例(instance)与对象(Object)
* 实例一般是指某一个构造函数创建出来的对象,我就称为某构造函数的实例
* 实例就是对象,对象是一个泛称
* 实例与对象是一个近义词
* 键值对与属性和方法
* 在js中键值对的集合称为对象
* 如果值为数据(非函数),就称该键值对为属性
* 如果值为函数(方法),就成该键值对为方法
* 父类与子类
* 传统的面向对象语言中使用类来实现继承,那么就有父类,子类的概念
* 父类又称为基类,子类又称为派生类
* 在js中常常称为父对象 子对象 即基对象 派生对象
2. 原型相关的概念
* 神秘对象针对构造函数称为 “原型属性" (神秘对象与构造函数的关系)
* 神秘对象就是构造函数的原型属性
* 简称原型
* 神秘对象与构造函数所创建出来的对象也有一定关系 (神秘对象与构造函数创建的对象的关系)
* 关系是什么
* 在该对象访问某一个方法或属性的时候,如果该对象中没有,就会到这个神秘对象中查找
* 神秘对象针对构造函数创建出来的对象称为原型对象
* 简称原型
* 对象继承自其原型(什么是原型继承)
* 构造函数创建的对象继承自构造函数的原型属性
* 构造函数创建的对象 继承自该对象的原型对象
* 构成函数所创建出来的对象,与构造函数的原型属性表示的对象,是两个不同的对象
* 原型中的成员(无论方法和属性),可以直接被实例对象所使用
* 也就是说实例对象直接‘含有‘ 原型中的成员
* 因此实例对象继承自原型
* 这样的继承就是‘原型继承
## JS的对象比较
由于js是解释执行的语言,那么在代码中出现函数与对象如果重复执行,会创建多个副本
1. 由于js是解释执行的语言,
4. 传统的构造方法的定义方式会影响性能,容易造成多个对象有多个方法副本,应该将方法单独抽取出来。
5.可以考虑将方法全部放到外面,但是有安全隐患
* 在开发中会引入多个框架或库,自定义成员越多,出现命名冲突的几率越大
* 可能在开发中会有多个构造函数,每一个构造函数应该有多个方法,那么就hi变得不容易维护
6. 任意一个对象都会默认的链接到它的原型中
* 创建一个函数,会附带的创建一个特殊的对象,该对象使用函数.prototype引用,称其为函数的原型属性
* 每一个由该函数作为构造函数创建的对象,都会默认的链接到该对象上
* 在该对象访问某一个方法或属性的时候,如果该对象中没有,就会到这个神秘对象中查找
* 如果访问数据,当前对象中如果没有该数据就到构造函数的原型属性中去找
* 如果写数据,当对象中有该数据的时候,就是修改值。如果对象没有该数据,那么就添加值
|
#对象的原型链 |
1. 凡是对象就有原型
2. 原型也是对象
3. 凡是对象向就有原型,原型又是对象,因此凡是给定义一个对象,那么就可以找到他的原型,原型还有原型,那么如此下去,就构成一个对象的序列,称该结构为原型链
|
#原型链结构 |
1. 凡是使用构造函数,创建出对象,并且没有利用赋值的方式修改原型,就说该对象保留默认的原型链
2.默认原型链的机构
当前对象 →构造函数.prototype→Object.prototype→null
3. 实现继承的时候,有时会利用替换原型链结构的方式实现原型继承,那么原型链结构就会发生改变
|
#原型式继承 |
1. 所谓的原型式继承就是利用修改原型链的结构(增加一个节点,删除一个节点,修改节点中的成员)来使得实例对象可以使用整条原型链中的所有成员
2. 使用规则必须满足属性搜索原则
|
#函数的构造函数Function |
1. 在js中使用Function可以实例化函数对象,在js中函数与普通对象一个也是一个对象类型,函数是js中的一等公民
2. 函数是对象,就可以使用对象的动态特性
3. 函数是对象,就有构造函数创建函数
4. 函数是函数,可以创建其他对象
5. 函数是唯一可以限定变量作用域的结构
##函数Function的实例
new Function();
1. Function中的参数全是字符串
2. 该构造函数的作用是将参数链接起来组成函数
* 如果参数只有一个,那么表示函数体
* 如果参数有多个,那么最后一个参数表示新函数体,前面的所有参数表示函数的新参数
* 如果没有参数,表示创建一个空函数
|
#arguments对象 argument(参数)
1. arguments是一个伪数组对象,表示在函数调用的过程中传入的所有参数的集合
* 在函数调用的过程中没有规定参数的个数与类型,因此函数调用就具有灵活的特性,那么为了方便使用,
* 在每一个函数调用的过称重,函数代码体内有一个默认的对象arguments,它存储着实际传入的所有参数
* 在代码设计中,如果需要函数带有任意个参数的时候,一般就不带任何参数,所有的参数利用arguments来获取
2. js中函数并没有规定必须如何传参,定义函数的不写参数一样可以调用时传递参数
3. 定义的时候写了参数,调用的时候也可以不传参
4. 定义的时候写了一个参数,调用的时候可以随意的传递多个参数
|
#this 指向 |
. 在js中使用Function可以实例化函数对象,在js中函数与普通对象一个也是一个对象类型,函数是js中的一等公民
2. 函数是对象,就可以使用对象的动态特性
3. 函数是对象,就有构造函数创建函数
4. 函数是函数,可以创建其他对象
5. 函数是唯一可以限定变量作用域的结构
|
#函数的原型链
|
1. 任意一个函数,都是相当于Function的实例 类似于{}与new Object()的关系
2. 函数的当成对象的时候,他的属性为__proto__
3. 函数的构造函数是什么?Function
4. 函数应该继承自‘Function.prototype‘
5. Function.prototype 继承自 Object.prototype
6. Object函数是大写Function的实例
7. Object作为对象 是继承自Function.prototype的,Function.prototype继承自Object.prototype
8.Function是自己的构造函数
|
#对象原型 |
1. 在js中任何对象的源点就是Object.prototype
2. 在js中任何函数的源点就是Function.prototype
|