摘要:隐式绑定即绑定到最顶层或最近调用对象上显式绑定即用或手动进行绑定方法实现绑定构造函数不存在其实在中不存在构造函数,我们所说的构造函数其实就是普通的函数,它只是用被构造调用而已。
JS是编译型语言
编译发生在代码执行前几微秒,简单来说就是js在执行前要进行编译,编译过程发生在代码执行前几微妙,甚至更短。
编译的步骤词法分析
以var a = 2 为例,词法分析会将其分成三个有意义的代码块即词法单元。
语法分析
将词法单元组合生成代表了程序语法的结构的树,即抽象语法书(AST)。
代码生成
将AST生成可执行的代码。即将AST转化成一组机器指令。
如果查找的目的是对变量进行赋值,那么就会使用 LHS 查询;如果目的是获取变量的值,就会使用 RHS 查询。
词法作用域决定于你在写代码时的块级作用域
优化依赖于词法的静态分析
eval with 会创建新的作用域在词法分析阶段,无法知道eval with会对作用域做怎样的修改,此时引擎不再对作用域进行任何优化
函数作用域 函数声明 函数表达式区分函数声明和表达式最简单的方法是看 function 关键字出现在声明中的位
置(不仅仅是一行代码,而是整个声明中的位置)。如果 function 是声明中
的第一个词,那么就是一个函数声明,否则就是一个函数表达式。
隐式的生成块级作用域
不存在变量提升
提升 原因变量(包括函数在内)的所有声明都会优先执行,只有声明本身会提升,而赋值或其他运行逻辑会留在原位置
过程这意味着无论作用域中的声明出现在什么地方,都将在代码本身被执行前首先进行处理。
可以将这个过程形象地想象成所有的声明(变量和函数)都会被“移动”到各自作用域的
最顶端,这个过程被称为提升。
声明本身会被提升,而包括函数表达式的赋值在内的赋值操作并不会提升。
当函数能够记住或访问所在的词法作用域,及时是被作用域外调用,就产生了闭包
模块现代模块机制
未来的模块机制
关于this 绑定时间点是在函数运行时绑定的,而非定义时。它的上下文取决于函数调用时的各种条件,和在哪里定义的没有关系,只取决于函数的调用方式。
绑定过程当函数被调用时,会创建一个执行上下文,在这个上下文里包含了函数在哪里没调用(调用栈),调用函数的方法,参数等。this作为执行上下文的一个属性,可以在函数执行的过程中用到。
绑定类型
默认绑定
即绑定到全局,严格模式下回绑定到undefined。
function foo() { console.log( this.a ); } var a = 2; (function(){ "use strict"; foo(); // 2 })()
隐式绑定
即绑定到最顶层(或最近调用对象)上
function fun() { console.log(this.a) } var obj2 = { a: 3, fun: fun, } var obj1 = { a: 2, obj2: obj2, } obj1.obj2.fun() // 3
显式绑定
即用call或apply手动进行绑定
bind方法实现
new绑定(构造函数)
不存在
其实在js中不存在构造函数,我们所说的构造函数其实就是普通的函数,它只是用new被“构造调用”而已。
new发生了什么?
创建(或者说构造)一个全新的对象。
这个新对象会被执行[[原型]]连接。
这个新对象会绑定到函数调用的this。
如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
箭头函数 =>
对象 内置对象基本类型在需要的时候(比如说获取长度),会被引擎默认转成内置对象,从而进行方法的调用。
基础类型并不是继承自内置对象
var strPrimitive = "I am a string"; typeof strPrimitive; // "string" strPrimitive instanceof String; // false var strObject = new String( "I am a string" ); typeof strObject; // "object" strObject instanceof String; // true Object.prototype.toString.call( strObject ); // [object String]null
typeof null === Object;
原理是这样的,不同的对象在底层都表示为二进制,在 JavaScript 中二进制前三位都为 0 的话会被判
断为 object 类型,null 的二进制表示是全 0,自然前三位也是 0,所以执行 typeof 时会返回“object”
浅拷贝
Object.assign({}, obj)
深拷贝
JSON.stringify
getOwnPropertyDescriptor(myObj, "a") defineProperty Object.defineProperty(myObj, "a", { value: 2, writable: true, configurable: true, enumerable: true })Getter 、Setter
var obj = { get a() { return this._a_ }, set a(val) { this._a_ = val * 5 } } obj.a = 10 console.log(obj.a) // 50 var obj2 = {} Object.defineProperty(obj2, "a", { get: function() { return this._a_ }, set: function(val) { this._a_ = val * 2 } }) obj2.a = 15 console.log(obj2.a) // 30存在性
in
"a" in obj1 会检查obj及其原型链上是否有"a"
hasOwnProperty
不会检查原型链,如果需要可以Object.prototype.hasOwnProperty.call(myObj, "a")
返回实例对象O的构造函数(的引用)。任何一个prototype对象都有一个constructor属性,指向它的构造函数,每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性
例如
function Test() { this.name = "test" } var test = new Test() console.log(test.constructor === Test) // true类constructor
构造函数 constructor 是用于创建和初始化类中创建的一个对象的一种特殊方法.
class Polygon { constructor() { this.name = "Polygon"; } } class Square extends Polygon { constructor() { super(); } } class Rectangle {} Object.setPrototypeOf(Square.prototype, Rectangle.prototype); console.log(Object.getPrototypeOf(Square.prototype) === Polygon.prototype); //false console.log(Object.getPrototypeOf(Square.prototype) === Rectangle.prototype); //true let newInstance = new Square(); console.log(newInstance.name); //Polygonproto
实例对象__proto__指向生成改对象的构造函数的原型
例如
|function Test() { this.name = "test" } Test.prototype = { color: "red" } var test = new Test() console.log(test.__proto__ === Test.prototype) // true console.log(test.__proto__)Object.create
var foo = { something: function() { console.log( "Tell me something good..." ); } }; var bar = Object.create( foo ); bar.something(); // Tell me something good... Object.create(..) 会创建一个新对象(bar)并把它关联到我们指定的对象(foo)
这样 我们就可以充分发挥 [[Prototype]]
机制的威力(委托)并且避免不必要的麻烦
(比如使 用 new 的构造函数调用会生成 .prototype 和 .constructor 引用)。
缺点
实例的属性都会指向同一个引用
实现
function Parent() { this.names = [1, 2, 3] } function Child() { } Child.prototype = new Parent() var child1 = new Child() var child2 = new Child() child1.names.push(4) console.log(child1.names) // [1,2, 3,4] console.log(child2.names) // [1,2, 3,4]借用构造函数
实现
function Parent() { this.names = [1, 2, 3] this.getName = function () { console.log(this.name) } } function Child() { Parent.call(this) } var child1 = new Child() var child2 = new Child() child1.names.push(4) console.log(child1.names) console.log(child2.names)
缺点
每个子实例都会实例化方法一次,内存爆炸
实现
function Parent() { this.names = [1, 2, 3] } Parent.prototype.getName = function () { console.log(this.names) } function Child() { Parent.call(this) } Child.prototype = new Parent() var child1 = new Child() var child2 = new Child() child1.names.push(4) child1.getName() child2.getName()
缺点
子类实例上有一份父类的属性,二者重复造成内存浪费
父类的构造函数被调用了两次
寄生式组合继承实现
function Parent() { this.names = [1, 2, 3] } Parent.prototype.getName = function () { console.log(this.names) } function Child() { Parent.call(this) } Child.prototype = Object.create(Parent.prototype) var child1 = new Child() var child2 = new Child() child1.names.push(4) child1.getName() child2.getName()
优点
属性不会再原型链上重复
js中的继承其实就是在对象间建立关联关系,而行为委托就是建立这种关联关系的纽带。
("原型")面向对象风格function Foo(who) { this.me = who; } Foo.prototype.identify = function () { return "I am" + this.me; }; function Bar(who) { Foo.call(this,who); } Bar.prototype = Object.create(Foo.prototype); Bar.prototype.speak = function () { alert("Hello," + this.identify() + "."); }; var b1 = new Bar("b1"); var b2 = new Bar("b2"); b1.speak(); b2.speak();对象关联风格
Foo = { init:function (who) { this.me = who; }, identify:function () { return "I am" + this.name } }; Bar = Object.create(Foo); Bar.speak = function () { alert("hello," + this.identify() + "."); }; var b3 = Object.create(Bar); b3.init("b3"); var b4 = Object.create(Bar); b4.init("b4"); b1.speak(); b2.speak();
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/97508.html
摘要:注此读书笔记只记录本人原先不太理解的内容经过阅读你不知道的后的理解。作用域及闭包基础,代码运行的幕后工作者引擎及编译器。 注:此读书笔记只记录本人原先不太理解的内容经过阅读《你不知道的JS》后的理解。 作用域及闭包基础,JS代码运行的幕后工作者:引擎及编译器。引擎负责JS程序的编译及执行,编译器负责词法分析和代码生成。那么作用域就像一个容器,引擎及编译器都从这里提取东西。 ...
摘要:异步请求线程在在连接后是通过浏览器新开一个线程请求将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件循环队列中。 基础:浏览器 -- 多进程,每个tab页独立一个浏览器渲染进程(浏览器内核) 每个浏览器渲染进程是多线程的,主要包括:GUI渲染线程 JS引擎线程 也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎) JS引擎线程负...
摘要:如果存在于原型链上层,赋值语句的行为就会有些不同。中包含的属性会屏蔽原型链上层的所有属性,因为总是会选择原型链中最底层的属性。如果不直接存在于中而是存在于原型链上层时会出现的三种情况。类构造函数原型函数,两个函数通过属性和属性相关联。 1 [[Prototype]] 对于默认的 [[Get]] 操作来说,如果无法在对象本身找到需要的属性,就会继续访问对象的 [[Prototype]] ...
摘要:如果存在于原型链上层,赋值语句的行为就会有些不同。中包含的属性会屏蔽原型链上层的所有属性,因为总是会选择原型链中最底层的属性。如果不直接存在于中而是存在于原型链上层时会出现的三种情况。类构造函数原型函数,两个函数通过属性和属性相关联。 1 [[Prototype]] 对于默认的 [[Get]] 操作来说,如果无法在对象本身找到需要的属性,就会继续访问对象的 [[Prototype]] ...
摘要:闭包在循环中的应用延迟函数的回调会在循环结束时才执行事实上,当定时器运行时即使没给迭代中执行的是多有的回调函数依然是在循环结束后才会被执行,因此会每次输出一个出来。 闭包在循环中的应用 延迟函数的回调会在循环结束时才执行;事实上,当定时器运行时即使没给迭代中执行的是 setTime(..., 0),多有的回调函数依然是在循环结束后才会被执行,因此会每次输出一个6出来。 for(var...
showImg(https://segmentfault.com/img/bVbtd6M?w=1181&h=1365); showImg(https://segmentfault.com/img/bVbtd6T?w=1243&h=1984);
阅读 3863·2021-09-23 11:51
阅读 3057·2021-09-22 15:59
阅读 855·2021-09-09 11:37
阅读 2062·2021-09-08 09:45
阅读 1259·2019-08-30 15:54
阅读 2055·2019-08-30 15:53
阅读 484·2019-08-29 12:12
阅读 3282·2019-08-29 11:15