你不知道的JS - 上卷
文章目录
- 作用域和闭包
- 一、编译原理
- - 编译器的两种查询方式
- - 区分查询方式的意义
- 二、欺骗词法作用域
- - eval
- - with
- 三、块级作用域
- - for/if
- - 会生成块级作用域的语法
- 四、模块
- - 核心概念
- - 特征
- this和原型对象
- 一、this绑定规则
- - 默认绑定
- - 隐式绑定
- - 显式绑定
- - new绑定
- 二、对象
- - null的问题
- - 内置对象
- - 基本数据类型和包装类的区别
- 三、对象特性
- - 属性描述符
- - 不变性
- 四、属性访问
- - [[Get]]和[[Put]]
- - Getter和Setter
- - 属性的存在性(属性不存在还是值为undefined)
- 五、原型链
- - 关于原型继承和构造函数
- - 判断原型链的关联关系
- - __proto__的内部实现
- - 字典
作用域和闭包
一、编译原理
- 编译器的两种查询方式
1- LHS, 赋值操作 试图找到变量的容器本身(并不关心原本的值是什么)
2- RHS, 取值操作 试图得到某某的值
- 区分查询方式的意义
1- 对未声明的变量进行RHS时无法找到该变量(导致报错)
2- 对未声明变量进行LHS查询时如果找不到目标变量会在全局作用域中创建一个该名称的变量(严格模式下会报错)
二、欺骗词法作用域
- eval
1- 接收一个字符串作为参数,作用域为当前作用域(eval执行时的作用域)
2- 严格模式中eval运行时产生独立的作用域,在其中的变量声明是局部变量
var eval_b = 2;
var eval_str = 'var b = 3'
function eval_foo(str,a) {eval(str); // 相当于在此处执行 var b = 3;console.log(a,b); // 输出的b为内部作用域的b
}
eval_foo(eval_str,1);
console.log('----------------- eval');
- with
将代码运行在指定的作用域中, with运行时拥有独立的作用域
var eval_b = 2;
var eval_str = 'var b = 3'
function eval_foo(str,a) {eval(str); // 相当于在此处执行 var b = 3;console.log(a,b); // 输出的b为内部作用域的b
}
eval_foo(eval_str,1);
console.log('----------------- eval');
三、块级作用域
- for/if
1- for/if并不会生成独立的作用域,因此无论是在for循环头部定义的变量还是在{}内定义的变量都会挂载在外部作用域上
2- 变量声明应该距离使用的地方越接近越好,并最大限度地本地化
function forValue() {var forName = 9;for(forName = 0; forName < 1; forName++) {// for并未生成独立的作用域, 因此forName2会挂载在外部作用域上var forName2 = 100; console.log('for-inner',forName,forName2);}// 覆盖了同名变量,for内部定义的变量也会成为当前作用域下的变量console.log('for-ourter',forName,forName2); console.log('---块级作用域-for')
}
forValue()
- 会生成块级作用域的语法
1- with()
2- try-catch的catch分句
3- let / const
四、模块
- 核心概念
var MyModules = (function Manager() {var modules = {};// 模块名字, 依赖的模块列表, 包装函数function define(name, deps, impl) { for (var i = 0; i < deps.length; i++) {// --给依赖的模块列表加入之前已经有的模块deps[i] = modules[deps[i]]; }// 将依赖的模块列表传给包装函数modules[name] = impl.apply(impl, deps);}function get(name) {return modules[name];}return {define: define,get: get};
})();// 定义一个模块
MyModules.define('bar',[],function() {function hello(who) {return 'let me introduce: ' + who;}return { // 返回一个对象hello: hello};
});
// 取出一个模块
var bar = MyModules.get('bar');// 定义一个模块,依赖bar模块
MyModules.define('foo',[bar],function() {})
- 特征
**1- **为创建内部作用域而调用了一个包装函数;
**2- **包装函数的返回值必须至少包括一个对内部函数的引用,这样就会创建涵盖整个包装函数内部作用域的闭包。
this和原型对象
一、this绑定规则
- 默认绑定
直接调用时默认绑定到window上(严格模式中this会绑定到undefined上)
如:seterInterval() ===> window.seterInterval(); // 两者等价
- 隐式绑定
被谁调用this指向谁(受调用链上最后一层调用影响),使用别名调用函数时可能会被绑定到window上
// 隐式丢失问题
function foo() {console.log(this.a);
}
var obj = {a: 2,foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性
// "oops, global",在window下调用,、
bar();
// "oops, global",传入函数引用,相当于在window下调用
setTimeout( obj.foo, 0 );
- 显式绑定
call, apply, bind, 一参传null时实际是进行默认绑定到window上;
// 硬绑定, 固定this指向
function foo() {log(this.a);
}
function bar() {foo.call(obj); // 利用一个包装函数把作用域绑定到指定对象上
};
var obj = {a: 2
};
bar(); // 2, 作用域绑定在了固定的对象上
- new绑定
创建新对象 - 绑定原型链 - 绑定this(新对象) - 返回对象; 实际上并没有什么所谓的构造函数,只是对函数的"构造调用"
二、对象
- null的问题
typeof null; // 'object'
原理: 不同的对象在底层都表示为二进制,在 JavaScript 中二进制前三位都为 0 的话会被判断为 object 类型, null 的二进制表示是全 0,自然前三位也是 0,所以执行 typeof 时会返回“ object ”。
- 内置对象
1- 内置对象都是Object的子类型
2- 包括: String, Number, Boolean, Object, Function, Array, Date, RegExp, Error
3- 这些内置对象实际上是一些内置函数,经常被当做构造函数使用
4- 对于Object, Array, Function, RegExp来说, 无论使用字面量还是构造形式,创建出来的都是对象而不是字面量
5- JS中没有类,只有对象
- 基本数据类型和包装类的区别
// 实际上一个字符字面量是无法改变的,重新赋值是销毁后新建
var str = 'i am a str';
typeof str; // 'string'
str instanceof String; // falsevar strObj = new String('i am a str');
typeof strObj; // 'object'
strObj instanceof String; // true
三、对象特性
- 属性描述符
var obj = {};
Object.defineProperty(obj,'a',{value: 2,writable: true, // 是否可写configurable: true, // 是否可配置,设置为false后不可逆enumerable: true // 是否可枚举
});
log(obj);
- 不变性
var obj = {};
try{// 1.使一个对象成为常量属性(不可修改,重定义或删除)Object.defineProperty(obj,'a',{value: 2,writable: false, // 不可写configurable: false // 不可配置})// 2.禁止对象添加新属性并保留已有属性Object.preventExtensions(obj); // 3.密封// 创建一个密封的对象,这个方法实际上会在目标对象上调用Object.preventExtensions()方法并把所有属性标记为不可配置Object.seal(obj); // 4.冻结// 冻结对象, 这个方法实际上会在一个现有对象上调用Object.seal()方法并把所有属性设置为不可写(依然是浅冻结)Object.freeze(obj); // 以上方法都是浅冻结,想要深度冻结一个对象(引用类型也不可改变)需要遍历出所有的对象对其冻结
}catch(e){}
四、属性访问
- [[Get]]和[[Put]]
var getObj = {a:1,c:undefined};
- [[Get]]
getObj.a
;
1, 在语言规范中,getObj.a在getObj上实际上是实现了[[Get]]操作(类似于函数调用[Get])
getObj.b
;
undefined, 对象内置的[[Get]]操作在对象中查找是否有名称相同的属性,有就返回,没有则会去原型链上查找,原型链上没有则返回undefined
getObj.c
;
undefined, c和b的返回值都是undefined但是获取b的值时进行了更复杂的操作- [[Put]] 被触发时大致会检查以下内容:
1.属性是否是访问描述符(是否设置setter/getter)?
2.writable == false?
3.没有上述情况将该值设置为属性的值
4.如果对象中没有这个属性会进行更复杂的操作
- Getter和Setter
1- ES5可以用getter和setter改写默认操作,但是只能应用在单个属性上
2- 给属性定义getter/setter时这个属性会被定义为"访问描述符",js会忽略它们的value和writable特性,取而代之的是set和get特性;
var getterObj = {a: 1,b:3,get a() {return 2;}
};
log(getterObj.a); // 2
getterObj.a = 3;
log(getterObj.a); // 2// 示例
var setterObj = {get a() {return this._a_; // 用一个中间变量传递},set a(val) {this._a_ = val * 2;}
}
- 属性的存在性(属性不存在还是值为undefined)
'a' in setterObj
in操作符会在原型链上查找
setterObj.hasOwnProperty('a')
只会在目标对象上查找
五、原型链
- 关于原型继承和构造函数
1- 原型链的尽头: 普通对象最终指向Object.prototype
2- 所谓原型继承(误): 绑定原型链时并没有完全复制原型链上的属性方法到目标对象,而是在两个对象间建立了连接,目标对象通过委托访问另一个对象(原型链)的属性和函数,所以继承的说法并不严谨
3- 所谓构造函数(误): 实际上JS中并没有什么所谓的构造函数,只是对函数的"构造调用(函数前加上new,new会劫持所有普通函数并用构造对象的形式来调用它)"
function doSomething(name) {this.name = name;log('假装自己是构造函数',name);
}
doSomething.prototype.show = function() {log(this.name);
}
var a = new doSomething('a');
a.show(); // 'a', a上并没有show方法,会通过委托访问到原型链上找
log(a.constructor == doSomething.prototype.constructor); // true, 同上, a.constructor指向一个函数并不是说这是a的构造函数,而是因为对.constructor这个方法的访问被委托到了原型链上!!!
a.__proto__ = Object.create(null); // 把a的原型链指向一个"空"对象
log(a.constructor); // undefined, 证明之前的.constructor操作是被委托到原型链上了
- 判断原型链的关联关系
a instanceof doSomething;
在 a 的整条 [[Prototype]] 链中是否有指向 doSomething.prototype 的对象?
doSomething.prototype.isPrototypeOf(a);
在 a 的整条 [[Prototype]] 链中是否出现过 Foo.prototype
- __proto__的内部实现
try{throw '__proto__的内部实现';Object.defineProperty(Object.prototype, "__proto__", {get: function () {return Object.getPrototypeOf(this);},set: function (o) {Object.setPrototypeOf(this, o);return o;}});
}catch(e) {log(e);}
- 字典
用Object.create(null)方法创建一个绝对空对象来存储数据,不会受到原型链的干扰,可称之为字典
你不知道的JS - 上卷相关推荐
- 读书笔记-你不知道的js(上卷)
你不知道的js 该书不是全面的讲解,这本书可以作为扫过基础知识后的提升和补充. 作者站在js原生语言的角度(而不是站在js营销的角度,营销使得js扭曲本身的含义去迎合其他语言的理解和使用习惯)去从新定 ...
- 【你不知道的JavaScript上卷】——作用域与闭包
原文: [你不知道的JavaScript上卷]--作用域与闭包 JS语言万变不离其宗,其中最常用.最重要的也就是常用的几个大概念.数据类型.作用域.原型链.闭包.this指针.异步,不同的人理解不一样 ...
- 对象----《你不知道的JS》
最近在拜读<你不知道的js>,而此篇是对于<你不知道的js>中对象部分的笔记整理,希望能有效的梳理,并且深入理解对象 一.语法 对象两种定义形式:声明(文字)形式.构造形式 声 ...
- 翻译连载 | 第 9 章:递归(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇...
为什么80%的码农都做不了架构师?>>> 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS> ...
- 翻译连载 | JavaScript轻量级函数式编程-第4章:组合函数 |《你不知道的JS》姊妹篇...
原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...
- 翻译连载 | 第 10 章:异步的函数式(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇...
为什么80%的码农都做不了架构师?>>> 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS> ...
- 翻译连载 | JavaScript轻量级函数式编程-第7章: 闭包vs对象 |《你不知道的JS》姊妹篇...
原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...
- 翻译连载 | 附录 A:Transducing(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇...
原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...
- 你不知道的JS之作用域和闭包(二)词法作用域
原文:你不知道的js系列 词法作用域(Lexical Scope) Lex time 一个标准的编译器的第一个阶段就是分词(token化) 词法作用域就是在词法分析时定义的作用域.换句话说,词法作用域 ...
最新文章
- 常用命令-临时记录!
- webpack 读取文件夹下的文件_TypeScript完全解读(26课时)_1.TypeScript完全解读-开发环境搭建...
- 每天一道LeetCode-----找到给定序列中所有和为某个值的集合或集合个数,序列中可以有/无重复项,集合元素顺序不同算不同集合等
- Python基础学习笔记之(二)
- matplotlib安装失败_Python | 安装中遇到“0x80072f7d 未指定的错误”
- server取出多个最小值 sql_SQL汇总查询及分组查询
- 机器学习没有捷径,根据机器学习算法地图学习是最有效的一种方式!
- 【个人笔记 - 目录】OpenCV4 C++ 快速入门 30讲
- jquery在选择元素的时候,可以写成var div=$(div)
- linux centos目录结构(一)
- win10计算机扫描,Win10系统下如何使用扫描仪
- java简单计算器实现
- Qt:34---MDI多文档界面
- 利用神经网络识别12306验证码——(四)数据增广以及训练集、验证集的划分
- 工业级嵌入式WiFi模块 无线网关智能家居 WiFi音视频传输模块 WiFi转有线网口模块方案
- maven plugins 飘红问题
- 大一作业HTML网页作业:中华传统文化题材网页设计(纯html+css实现)
- php imap 安装_php怎么安装imap扩展
- 阿里云盘内测申请_阿里云网盘公测预约开始了,现在申请还送2个T的空间!
- krait和kryo_回归四核的自主Kryo架构_手机_手机评测-中关村在线
热门文章
- 攻防世界:PWN刷题-forgot
- 基于快速GeoHash,如何实现海量商品与商圈的高效匹配
- 手机软件驱蚊真管用么?
- php社区团购小程序系统,[PHP程序]狮子鱼社区团购小程序V16.0.1独立版开源无授权带前端小程序...
- 牛奶百度收录批量查询工具【速度极快】
- 计算机文件名是蓝颜色,为什么有些文件名是蓝色的?
- Ubuntu16.04升级内核并卸载不常用软件
- 黄牛党自述:火车票哪儿去了以及如何买到火车票
- 只跟你分开一次 (极少转感情文字,这算一次吧)
- 利用Quartz2D设置自定义条纹背景