- 作用域是根据名称查找变量的一套规则
- LHS查询:对变量进行赋值
- RHS查询:获得变量的值
- 引擎: 编译+执行。
- 编译:1.词法化 2. 生成树状结构。
- 作用域由函数被声明时所处的位置决定,与在调用位置无关
- 改变词法作用域:1.eval(字符串),动态插入代码并执行.2.with 为对象访问属性提供速写方式. 两个方法都是在运行时改变作用域,导致引擎无法在编译时对作用域查找进行优化,最后性能下降,代码运行变慢。
- 函数表达式可以匿名,函数声明不可以匿名
创建块作用域
1.with
2.try/catch中的catch可以创建块作用域
3.let
4.const
5.立即执行函数
- let声明的变量不会进行变量提升,let会将定义的变量绑定在当前作用域,需要先声明后使用,否则会报错
块作用域有用的原因:
- 垃圾收集
- let循环
-
函数声明会被提升,函数表达式不会被提升
-
闭包: 函数持有其被最原始所在词法作用域的引用,即使函数在当前词法作用域外执行,闭包会阻止垃圾回收器释放该作用域所占的内存空间。闭包可以实现模块。
-
函数的作用域与函数声明的位置有关,this与函数声明的位置无关,this与函数的调用方式有关,也就是函数在调用时才有意义,属于运行时绑定。
-
当一个函数被调用时,会创建一个活动记录(执行上下文)。这个记录会包含函数在哪里被调用到(调用栈)、函数的调用方式、传入的参数等信息。this就是这个记录的一个属性,会在函数执行的过程中用到。
-
this是函数被调用时发生的绑定,它的指向完全取决于函数在哪里被调用。
-
bind,apply,call都会改变this的指向,区别在于bind会返回一个this绑定后的函数,不会立即执行。apply,call会立即执行该函数,apply第二个参数传得是数组,call是从第二个参数开始依次传入。
-
js里函数是一等公民,js里的构造函数与其他语言的类和构造函数完全不一样,js的构造函数只是一个普通函数,不会实例化某个类,没有“构造”功能,他们只是被new操作符调用的普通函数而已。
-
this绑定优先级,由高到低
- 函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象。
var bar = new foo()
- 函数是否通过call、apply(显式绑定)或者硬绑定调用?如果是的话,this绑定的是指定的对象。
var bar = foo.call(obj2)
- 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。
var bar = obj1.foo()
- 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。
var bar = foo()
- ES6中的箭头函数不会使用四条标准的绑定规则,而是根据当前的词法作用域来决定this,具体来说,箭头函数会继承外层函数调用的this绑定(无论this绑定到什么)。与self=this机制一样。
for..of 检查容器内的属性值。
for..in 检查容器内是否有某个值,实际上检查的是某个属性名是否存在,而不是属性值。
for..in 不但包含自己直接拥有的属性,也包含prototype上的属性(原型链查找),hasOwnProperty只包含自身对象直接拥有的属性。
Object.create(null)来创建的对象不会有原型对象,也不会有原型对象上的属性方法。如果想检查自身属性,可以用Object.prototype.hasOwnProperty.call(obj, “a”)来判断。
-
Object.keys()会返回一个数组,包含所有可枚举属性。
-
Object.getOwnPropertyNames()会返回一个数组,包含所有属性,无论是否可枚举
-
Object.keys()、Object.getOwnPropertyNames()只查找自身的属性
-
Obj.propertyIsEnumerable()方法
1.检查属性是否存在于对象中而不是原型链中
2.判断其enumerable是不是true
- Typeof null会返回字符串“object”,实际上,null本身是基本类型,原理是这样的,不同的对象在底层都表示为二进制,在Javascript中二进制的前三位都为0的话会被判断为object类型,null的二进制表示全为0,自然前三位也为0,所以执行typeof时会返回object
内置对象,名字看起来和简单基础类型一样(大小写不同),不过实际上它们的关系更复杂
- String
- Number
- Boolean
- Object
- Function
- Array
- Date
- RegExp
- Error 这些内置函数可以当作构造函数
- 浅复制只是复制对象的引用
Object.assign()
- 深复制:
var newObj = Object.assign
- Object.defineProperty()方法会直接在一个对象上定义一个新属性或者修改现有属性,并返回此对象。
Object.defineProperty(myObject, ”a”, {
value:2,
writable:true,
configurable:true,
enumerable:true//属性描述符(数据描述符)
});
-
writable决定是否可以修改属性的值,false时属性不可改变,相当于定义一个空操作的setter,严格模式下,setter会抛出一个TypeError的错误。
-
Configurable表示属性可配置,为false时不可修改属性描述符,不管在严格还是非严格模式下,修改 writable,configurable,enumerable都会报TypeError错误。修改属性值不会报错但是不会成功。所以,把configurable修改为false是单向操作,无法撤销。
-
Delete obj.a 可以删除对象可删除的属性(configurable为false时不可删除),但这不是释放内存,a删除后读取为undefined
-
Enumerable,属性是否会出现在对象的属性枚举中,比如for..in循环
- 结合writable:false 和configurable:false可以创建一个真正的常量属性,
- 如果想禁止一个对象添加新属性并且保留已有属性,可以使用Object.preventExtensions(obj)
- Object.seal()会创建一个密封的对象,这个方法实际会在一个现有的方法上调Object.preventExtensions(obj)并把现有属性标记为configurable:false,所以密封之后不能添加新属性也不能配置和删除任何现有属性
- (最高级别)Object.freeze()会创建一个人冻结对象,这个方法实际上会调用seal()并把所有数据访问标记为writable:false,这样就无法修改他们的值
-
obj.a // 实际上是在obj上实现了[[Get]]操作,获取属性值
-
访问不存在的对象/变量,会发生ReferenceError(作用域查找),访问不存在的属性,会返回undefined(原型链查找);对不存在的对象和属性进行方法调用,会发生TypeError。
-
如果已经存在这个属性,[[Put]]算法会检查下面的内容
- 属性是否是访问描述符?如果是并存在setter就调用setter
- 属性的数据描述符中writable是否是false?如果是,在非严格模式下静默失败,在严格模式下抛出TypeError异常。
- 如果都不是,将该值设置为属性的值。
-
getter和setter函数可以改写对象单个属性的[[get]] 和[[put]] 默认操作
-
访问描述符 / 访问器属性 (getter和setter) ,与属性描述符(数据描述符)相对
-
Getter是一个隐藏函数,会在获取属性值时调用,setter也是一个隐藏函数,会在设置属性值时调用。