你不知道的JS(上)
1 作用域
- referenceERROR, 会在strict模式下,LHS查询全局作用域不能找到抛出,非严格模式下则会创建; 还有在RHS查询一个未声明的变量时
- RHS查询完成后,如果对其进行不合理操作,那么就会报typeError错误(比如非函数类型做函数调用,引用null或者undefine值中的属性)c:果然是有静态分析的
2 词法作用域
2.1 词法阶段
- 查找,不知道vuex的store中的命名空间是怎样的实现?
2.2 欺骗词法
- 这eval()不是方便XSS吗…..
- with,非严格模式下,如果没有在自己特定作用域,所处函数作用域,全局作用域LHS都没找到,然后就自己创建了一个全局变量
3 函数作用域与块作用域
隐藏内部实现
函数作用域function和(function有着不同的作用, 后者作为函数表达式, 不需要显式地调用函数名。
匿名与具名
- 前者可以等同于具名函数表达式命名
立即执行函数表达式
- IIFE
- 两种写法(function(){}) (), (function(){}())
4 提升
- 变量和函数的声明会被提前, 而赋值和逻辑运算则会停在原地, 有时候会出现奇怪的错误; 函数提升优先级更高
5 闭包
定义: 函数, 在定义时的语法作用域以外的地方被调用; 访问定义时的词法作用域
闭包域作用域, 例子非常有意思; var, let
模块模式的两个必要条件:
- 必须有外部的封闭函数, 函数必须被调用一次(每次调用会产生新的实例
- 封闭函数必须返回至少一个内部函数,形成闭包并提供状态的作用接口
单例模式,IIFE即可完成;
命名公共API返回的对象
感觉和Java的类,实例,匿名类一个机制
1 关于this
- 不是调用自身而是调用
2 this全面解析
从调用位置来理解
绑定规则
默认绑定
- 默认绑定,严格模式下会无法绑定到全局变量中
隐式绑定
- 自适应绑定,类似同名变量在作用域中的覆盖;这里this绑定也会随着调用位置的被覆盖
- 参数赋值是一种隐性赋值,传入对象.函数,仅仅是传入函数,相当于是默认绑定
显式绑定
- call(其实现会通过强制转换为对象,this绑定到obj中)
- apply
- API调用上下文:通过添加一个参数来增加一个绑定对象(本质同call和apply)
new绑定
- 不存在构造函数,只有对函数的“构造调用”
优先级
- bind()的实现,会先检测是否被new调用?替换掉旧的硬绑定:维持不可修改的特性;为什么?因为需要传递调用时添加的参数给下层函数。
例外
- apply(null,[2,3])为了传递参数,null会默认
- 柯里化
- 创建DMZ对象,创建空对象作为this的空间;Object.create(null)
- 间接引用,默认绑定
- 软绑定,给内置bind包装了成了自适应类似DMZ的方法
this
- 箭头函数会从函数作用域寻找,this的绑定
- 作用域和箭头函数, 和传统的this机制
3 对象
type
- object type
属性和方法
- 函数永远不会“属于”一个对象; 虽然有些会有this绑定,但这是另一回事
数组:数值下标/值对
对象复制,c:vue-admin-template中有deepClone是通过键值对的拷贝,const;
- 翻译有失误,浅拷贝,深复制
- 浅拷贝的assign:将enumerable自有键复制到目标对象,使用=赋值
属性描述符: 所有属性都具备了属性描述符
不变性:
- 对象常量
- 禁止扩展
- seal:调用2并,设置configable:false;
- freeze:调用3并设置writable:false;大概是最高级别的不可变,引用的对象可以遍历引用对象,全部freeze
get 一个undefined确实够有趣的;put应用依然不太明白;(名称:_a_只是命名惯例)
存在性:in和hasproperty区;in是属性是否存在对象极其原型链(c:in的多义颇有*在c中的1/100风采)
Symbol.iterator 与内置的@@iterator(一个迭代器函数对象的汉顺),for of 的本质和改写;
为什么永不结束就会挂起
混合对象“类”
类也只是一种基础设计模式
JS只是模拟类的
C:编程和建筑非常像….似乎的确如此
这里的多态解释:继承链中不同层次的一个方法名被多次定义,调用方法时会自动选择合适的定义
JS中类与构造函数,与真正的类有所不同
方法继承和重写(多态)取决于你在哪个类的实例中使用;重写不会影响到父类,子类只是一个副本;继承的本质是复制。
多重继承…..好吧我对面向对象实在很不熟悉。
**JS中只有对象,通过关联来连接 **。
多态
- 通过call来将this绑定当前context,实现显式的多态
- 先复制,再进行特殊化覆盖….c:真是朴素的方法. 函数的引用问题怎么解决呢?共享一个函数
- 寄生继承:完全没明白
复制对象引用,而不能复制对象本身
5 原型链
- Object.prototype原型链的最终节点
- 底层会覆盖上层的同名,选择最近,
只存在上层prototype的三种情况:
- 默认,直接添加到当前对象,屏蔽属性
- writable:false ,strict 会报错,非~会被忽略(禁用父类的属性“继承”,但是可以通过defineProperty
- 上层存在且有setter,那么会调用setter
隐式屏蔽,obj.a++ ==> obj.a = obj.a + 1 会调用[[put]]
“类”
- 调用new Foo()创建 的每个对象将最终被[[prototype]]链接到Foo.prototype对象;并非直接关联到对象,而是通过关联到其他对象的新对象
- 原型继承(c:容易造成语义上的误解)
- 继承no => 通过委托访问属性和函数
- new 的机制很有意思,即便new一个普通函数,也会出现构造函数的效果,或者说new 时,函数调用会变成构造函数调用
- 所以constructor只是属性名,空白对象没有(可以自己定义),最终会沿着prototype到Object中
- 这里直接通过Object.create(obj.prototype)新建对象来关联期待的对象……这个真的很奇怪
- 两种错误示范写法:Bar.prototype = Foo.prototype 直接引用了Foo,任何修改都会同步到Foo
- Bar.prototype = new Foo() new作为构造函数的触发关键词,会有造成构造函数的副作用
- ES6可以直接修改prototype – Object.setPrototypeOf( source.prototype, target.prototype)
- 检查实例的继承祖先(内省、反射); isPrototypeOf ES5: proto 的奇怪属性(应该是getter+setter
对象关联
- Object.create(null)对象无法进行委托,instance of 检测不到原型链
行为委托
原型链的本质就是对象之间的关联关系
OLOO
- 最好将状态保存到委托者,而不是委托目标
- 避免同名函数,不是面向对象重写
- 在本身找不到时会使用委托
的确在理解了JS的原型链机制后,再用它来写java风格,就很奇怪….
两张图就很清晰了
C: 其实这种语言特性在业务开发中,表面上不懂也没事,常用的框架将关注点集中到了解决问题上,只需要按照需求填入内容即可,做出的能用,但是一旦出现性能瓶颈,框架考虑范围外的事情(这里其实涉及到泛用性和专用,自由度等的取舍博弈),这时候就会有对底层原理(相对问题而言)理解有所要求。经验和原理同样重要,两者都需要花费时间成本,大量的练习,才能相互印证真正理解why。
C:DRY其实也包含工作内容,当融会贯通,然后继续重复就毫无意义(边际成本)
蓝图设计(设计文档)果真就能反应设计的好坏,最起码清晰,层次分明
- 类语法糖,让JS看起来真像那么回事儿;但还是有之前的问题,只是语法上更加友好了
- 委托轻松了很多,但是命名该怎么解决呢?每一次都换名吗?
构造和初始化分离
两种风格的比较:委托更像是灵活的组合
ES6更好的语法:
- class减少使用’,’,简洁声明方法减少function
- 可以轻便地关联对象–setPrototypeOf()
- 简洁方法的副作用,默认返回匿名函数
- 没太明白匿名函数递归是怎么回事?
instanceof 内省, 它的表面含义与实际语言机制的不同,尽管结果是符合表面含义
鸭子类型,JS真是欢乐之源; Promise
A
优点:
- 通过extend,扩展了对象类型
- class中只能声明方法,减少了犯错的可能
- 构造函数问题解决
缺点
- 本质上仍然是prototype的一种语法糖,是实时的委托而不是复制
- 共享和私有的实现
- 同名属性和方法的屏蔽
- super中的this的动态绑定
- toMethods方法绑定到原方法
class真是个大坑….而且真是别扭