不知道的JavaScript
你不知道的JavaScript上卷笔记
前言
You don't know JavaScript是github上一个系列文章
初看到这一标题的时候,感觉怎么老外也搞标题党,用这种冲突性比较强的题目吸引眼球,以致最初真没去看内容。直到出了中文版《你不知道的JavaScript》,一看评价大家都说好,买来一读,内容果然很好,很多地方,让我这个半路转行JavaScript的人豁然开朗。中文版现在出了上卷,中卷应该很快会推出,下卷就要等久一点了
作用域和闭包
作用域是什么
1.现代JavaScript已经不再是解释执行的,而是编译执行的。但是与传统的编译语言不同,它不是提前编译,编译结果不能进行移植。编译过程中,同样会经过分词/词法分析,解析/语法分析,代码生成三个阶段。
2.以var a = 2;语句为例,对这一程序语句对处理,需要经过引擎,编译器,作用域三者的配合。其中,引擎从头到尾负责整个javascript程序的编译和执行过程;编译器负责语法分析和代码生成;作用域负责收集并维护由所有声明的标识符组成的系列查询,并实施一套规则,确定当前执行的代码对这些标识符的访问权限。
3.对于var a = 2;编译器首先查找作用域中是否已经有该名称的变量,然后引擎中执行编译器生成的代码时,会首先查找作用域。如果找到就执行赋值操作,否则就抛出异常
4.引擎对变量的查找有两种:LHS查询和RHS查询。当变量出现中赋值操作左侧时是LHS查询,出现中右侧是RHS查询
词法作用域
1.词法作用域就是定义在词法阶段的作用域。词法作用域是由你在写代码时将变量和块作用域写在哪里决定的,词法处理器分析代码时会保持作用域不变
function foo(a){
var b = a * 2;
function bar(c){
console.log(a,b,c);
}
bar(b * 3);
}
foo(2);这个例子有三级嵌套的作用域
- 作用域查找会在找到第一个匹配的标识符时停止
- eval和with可以欺骗词法作用域,不推荐使用
函数作用域和块作用域
- JavaScript具有基于函数的作用域,属于这个函数的变量都可以在整个函数的范围内使用及复用
(function fun(){})()
函数表达式和函数声明的区别是看function关键字出现在声明中的位置。如果function是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式- with,try/catch具有块作用域,方便好用的实现块级作用域的是es6带来的let关键字
提升
1.如下示例
a=2;
var a;
console.log(a);
上述代码输出的不是undefined,而是2。因为上述代码需要经过编译器的编译,编译过程中首先会进行变量声明,然后再由引擎进行变量赋值,所以,上述变量声明虽然写在第二行,但是声明过程是首先执行的
console.log(a);
var a = 2;上述代码不是抛ReferenceError异常, 而是输出undefined
- 只有声明会被提升,赋值及其他运行逻辑会留在原地
foo();
function foo(){
console.log(a);//undefined
var a = 2;
}foo();//不是ReferenceError,是TypeError var foo = function bar(){}; foo被提升并分配给所在作用域,所以foo()不会导致ReferenceError,但是foo没有被赋值,对undefined进行函数调用,所以抛出TypeError
作用域闭包
- 将内部函数传递到所在词法作用域以外,它都会持有对原始定义作用域的饮用,无论中何处执行这个函数都会使用闭包
- 本质上,无论何时何地,如果将函数当作第一级的值类型并到处传递,就会看到闭包在这些函数中的应用。在定时器,事件监听器,ajax请求,web workers或者其他任何异步任务中,只要使用了回调函数,实际上就是在使用闭包
- 模块的封装利用了闭包,将内部变量隐藏,并返还一个公共api的对象,这一返回的对象对模块的私有变量形成闭包访问。
动态作用域
1.词法作用域是一套引擎如何寻找变量以及会在何处找到变量的规则。词法作用域最重要的特征是它的定义过程发生中代码的书写阶段
2.动态作用域让作用域作为一个在运行时就被动态确定的形式,而不是在写代码时进行静态确定的形式。
function foo(){
console.log(a);//2
}
function bar(){
var a = 3;
foo();
}
var a = 2;
bar();
词法作用域让foo()中的a通过RHS引用到了全局作用域中的a,所以输出2;动态作用域不关心函数和作用域如何声明以及在何处声明,只关心从何处调用。换言之,作用域链是基于调用栈的,而不是代码中的作用域嵌套。如果以动态作用域来看,上面代码中执行时会输出3
3.JavaScript不具备动态作用域,但是this机制中某种程度上很像动态作用域,this关注函数如何调用。
this词法
- es6通过箭头函数,将this同词法作用域联系起来了。
- 之前如果会遇到this丢失,常见方法时使用本地变量替换this引用
var obj = {
msg : 'awesome',
cool:function(){
setTimeout(function timer(){
console.log(this.msg);
},100);
}
};
var msg = 'not awesome';
obj.cool(); //not awesome var obj = {
msg : 'awesome',
cool:function(){
var self = this; setTimeout(function timer(
console.log(self.msg);
},100);
}
};
var msg = 'not awesome';
obj.cool(); //awesome var obj = {
msg : 'awesome',
cool:function(){ setTimeout(() => {
console.log(this.msg);
},100);
}
};
var msg = 'not awesome';
obj.cool(); //awesome var obj = {
msg : 'awesome',
cool:function(){
setTimeout(function timer() {
console.log(this.msg); }.bind(this),100);
}
};
var msg = 'not awesome';
obj.cool(); //awesome
this和对象原型
this全面解析
- 默认绑定。
function foo(){
console.log(this.a);
}
var a = 2; foo();//2上述示例中,函数调用应用了默认绑定,this指向全局对象
- 隐式绑定。调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。
function foo(){
console.log(this.a);
}
var obj={
a:2,
foo:foo
};
obj.foo();//2foo()被调用时,落脚点指向obj对象。当函数引用有上下文对象时,隐式对象将函数调用中的this绑定到上下文对象。
- 显示绑定。使用apply,call或者bind显示绑定
- new绑定。
- JavaScript中的构造函数只是一些使用new操作符时被调用的函数。它们并不会属于某个类,也不会实例化一个类。实际上,它们甚至不能说是一种特殊的函数类型,它们只是被new操作符调用的普通函数。
- 使用new来调用函数,或者说发生构造函数调用,会自动执行下面操作:a)创建一个全新的对象 b)这个新对象会被执行[[原型]]连接 c)这个新对象会绑定到函数调用的this d)如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象
- 优先级。new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
对象
- 属性描述符
var obj = {};
Object.defineProperty(
obj,
"a",
{
value:2,
writable:true,
configurable:true,
enumerable:true
}
);
obj.a;//2- writable决定是否可以修改属性的值,writable为false时,对obj.a的修改会静默失败。
- configurable表示属性是否可以配置,如果为false,再对obj调用defineProperty来修改设置,会抛出TypeError
- enumerable表示属性是否会出现在对象枚举属性中,如果为false,就不会出现在for...in循环中
- 对象常量。结合使用writable:false和configurable:false可以创建一个真正的常量属性(不可修改,不可重定义,不可删除)
- 禁止扩展。如果想禁止对一个对象添加新属性并且保留已有属性,可以使用Object.preventExtensions()方法
- 密封。Object.seal()会创建一个密封的对象,这个方法实际上会在一个现有对象上调用Object.preventExtensions()方法,并且把所有属性标记为configurable:false
- 冻结。Object.freeze()会创建一个冻结对象,这个方法会在现有对象上调用Object.seal()方法,并把所有数据访问的属性标记为writable:false
- [[get]],[[put]]。
var obj = {
this._a_ = 2;
get a(){ return this._a_; },
set a(val){ this._a_ = val * 2; }
};
Object.defineProperty(
obj,
"b",
{
get:function(){return this._a_ * 2},
enumerable:true
}
)
原型
本节为觉得书中写的有点绕,不清晰。推荐阅读
- constructor, prototype, proto 详解
- JavaScript中proto与prototype的关系
- How does proto differ from constructor.prototype?
- 理解JavaScript面向对象的思路
行为委托
- [[prototype]]机制是对象中一个内部链接引用另一个对象 如果中第一个对象上没找到需要的属性或者方法引用,引擎就会继续在[[prototyoe]]关联的对象上进行查找。同理,如果后者中也没找到需要的引用,就会继续查找它的[[prototype]],以此类推,这一系列对象的链接称为原型链。换言之,这个机制就是对象之间的关联关系。
- [[prototype]]是一种不同于类的设计模式,它是一种委托行为的设计模式。
不知道的JavaScript的更多相关文章
- js值----你所不知道的JavaScript系列(6)
1.数组 在 JavaScript 中,数组可以容纳任何类型的值,可以是字符串.数字.对象(object),甚至是其他数组(多维数组就是通过这种方式来实现的) .----<你所不知道的JavaS ...
- js类型----你所不知道的JavaScript系列(5)
ECMAScirpt 变量有两种不同的数据类型:基本类型,引用类型.也有其他的叫法,比如原始类型和对象类型等. 1.内置类型 JavaScript 有七种内置类型: • 空值(null) • 未定义( ...
- 闭包----你所不知道的JavaScript系列(4)
一.闭包是什么? · 闭包就是可以使得函数外部的对象能够获取函数内部的信息. · 闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. · 闭包就 ...
- let和const----你所不知道的JavaScript系列(2)
let 众所周知,在ES6之前,声明变量的关键字就只有var.var 声明变量要么是全局的,要么是函数级的,而无法是块级的. var a=1; console.log(a); console.log( ...
- LHS 和 RHS----你所不知道的JavaScript系列(1)
变量的赋值操作会执行两个动作, 首先编译器会在当前作用域中声明一个变量(如果之前没有声明过), 然后在运行时引擎会在作用域中查找该变量, 如果能够找到就会对它赋值.----<你所不知道的Ja ...
- Lodash 严重安全漏洞背后 你不得不知道的 JavaScript 知识
摘要: 详解原型污染. 原文:Lodash 严重安全漏洞背后 你不得不知道的 JavaScript 知识 作者:Lucas HC Fundebug经授权转载,版权归原作者所有. 可能有信息敏感的同学已 ...
- 【转载】14个你可能不知道的 JavaScript 调试技巧
了解你的工具可以极大的帮助你完成任务.尽管 JavaScript 的调试非常麻烦,但在掌握了技巧 (tricks) 的情况下,你依然可以用尽量少的的时间解决这些错误 (errors) 和问题 (bug ...
- 14 个你可能不知道的 JavaScript 调试技巧
了解你的工具可以极大的帮助你完成任务.尽管 JavaScript 的调试非常麻烦,但在掌握了技巧 (tricks) 的情况下,你依然可以用尽量少的的时间解决这些错误 (errors) 和问题 (bug ...
- 你所不知道的JavaScript数组
相信每一个 javascript 学习者,都会去了解 JS 的各种基本数据类型,数组就是数据的组合,这是一个很基本也十分简单的概念,他的内容没多少,学好它也不是件难事情.但是本文着重要介绍的并不是我们 ...
- 10个你可能不知道的JavaScript小技巧
1.变量转换 看起来很简单,但据我所看到的,使用构造函数,像Array()或者Number()来进行变量转换是常用的做法.始终使用原始数据类型(有时也称为字面量)来转换变量,这种没有任何额外的影响的做 ...
随机推荐
- php导出excel数据
提供两种导出excel方法 1 最简单的导出excel header('Content-Type: application/vnd.ms-excel'); //设置文件类型 也可以将 vnd.ms ...
- MSSQL - 因为数据库正在使用,所以无法获得对数据库的独占访问权。
关于“因为数据库正在使用,所以无法获得对数据库的独占访问权”的最终解决方案 今天在使用SQL Server2005对某个数据库进行还原操作的时候,出现了如上问题,“因为数据库正在使用,所以无法获得 ...
- AOP编程,spring实现及JDK,CGLIB实现
什么是AOP? AOP(Aspect-OrientedProgramming,面向方面编程)和OOP(Object-Oriented Programing,面向对象编程)思想不同,两者并非对立关系,前 ...
- oracle flashback 2
Flashback database After oracle 10g, oracle can rollback to an prior time by flashback databas ...
- web端、android端的文件上传
1.web端的文件上传. 这里是利用了第三方的jar包.这里所需要的jar包我已经上传到本博客的资源里了,以下是连接 http://download.csdn.net/detail/caihongsh ...
- bonjour
首先bonjour并非必须的,可是它的确非常方便,假设没有它我们须要指定ip地址进行局域网的传输,有了它就能够依据服务的详细的名称来选择服务,能够这样来理解bonjour就相当于hostname,我们 ...
- static作用
C程序一直由下列部分组成: 1)正文段——CPU运行的机器指令部分:一个程序仅仅有一个副本:仅仅读,防止程序因为意外事故而改动自身指令: 2)初始化数据段(数据段)——在程序中全部 ...
- SVM(支持向量机)(一)
(整理自AndrewNG的课件,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) SVM(Support Vector Machines)系列会循序 ...
- poj 3082多边形相交 'Roid Rage
题意是判断多边形是否相交 主要的思路就是判断每一个点是否在另外的多变形内 判断一个点是否在另一个多边形内主要思路是: 判断的那个点向左边做射线,如果射线与多边形的交点为奇数个则在多边形内,偶数个则不在 ...
- Redis C客户端API - God's blog - 博客频道 - CSDN.NET
Redis C客户端API - God's blog - 博客频道 - CSDN.NET Redis安装步骤: 1.redis server安装 wget http://redis.googlecod ...