你不知道的javascript(上卷)读后感(二)
this词法
熟悉ES6语法的开发者,箭头函数在涉及this绑定时的行为和普通函数的行为完全不一致。跟普通this绑定规则不一样,它使用了当前的词法作用域覆盖了this本来的值。
误解
this理解成指向函数本身,函数对象的属性(Fun.X)不是this.X 【X】
this指向函数的作用域 【X】
this是运行时进行绑定的,而不是编写时绑定的,它的执行上下文取决于函数调用时的各种条件,this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式(调用位置),具有动态性,简单地说,函数执行过程中的调用位置决定了this的绑定对象。
何为执行上下文?
函数被调用时,会创建一个活动记录,这个也称作执行上下文,这个记录包含了函数在哪里被调用(调用栈)、函数调用方式、传入的参数等信息,而this就是执行上下文的一个属性,函数调用时会被用到。
this绑定规则
- 默认绑定,this指向全局对象(严格模式,不允许使用)
- 隐式绑定,会把函数调用的this绑定到所在的上下文对象,对于隐式绑定,常常伴随着隐式丢失的问题,
函数别名、传入回调函数、函数传入内置函数均会造成此问题- 显式绑定,就是常见
call、apply方法,仍无法解决绑定丢失问题,但有个变种方法,叫做硬绑定
└── 硬绑定,函数bar中强制绑定(显示绑定)绑定foo在obj中执行
├── 包裹函数
├── 辅助函数(bind基本实现原理,下面有详细实现)
- new绑定,过程如下(发生在构造函数调用时):
1、创建(或者说构造)一个新对象
2、这个新对象会被执行[[Porototype]]连接
3、这个新对象会被绑定到函数调用的this
4、如果函数没有返回其他的对象,那么new表达式的函数调用会自动返回新对象
规则优先级:默认绑定 < 隐式绑定 < 显式绑定 < new绑定
☆☆☆☆☆☆ 辅助函数与bind函数的实现 ☆☆☆☆☆☆
function bind(fn, obj) {
return function() {
fn.apply(obj, arguments);
}
}
摘自MDN:
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
// this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
// 关键代码,如果是new调用,就是使用新创建的this替换硬绑定的this,说明了new绑定优先级大于硬绑定
return fToBind.apply(this instanceof fBound
? this
: oThis,
// 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
aArgs.concat(Array.prototype.slice.call(arguments)));
};
// 维护原型关系
if (this.prototype) {
fNOP.prototype = this.prototype;
}
// 下行的代码使fBound.prototype是fNOP的实例,因此
// 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
fBound.prototype = new fNOP();
return fBound;
};
}
更安全的this
bind辅助函数修改改变this的同时,可以使用Object.create(null),如.call/.apply(Object.create(null), ...argument),这样可以有效防止修改全局对象。
被忽略的this
null/undefined作为this的绑定对象传入call、apply,使用的是默认绑定规则。
软绑定
上述对硬绑定的介绍,会强制把this强制绑定到指定的对象上,防止了函数调用应用默认绑定规则,但是造成的问题就是,不够灵活,使用不了隐式绑定和显式绑定来修改this,于此同时,软绑定便出现了。
if (!Function.prototype.softBind) {
Function.prototype.softBind = function(obj) {
var fn = this;
// 捕获所有curried参数
var curried = [].slice.call(arguments, 1);
var bound = function() {
return fn.apply(
(!this || this === (window || global)) ? obj : this,
curried.concat.apply(curried, arguments)
);
}
bound.prototype = Object.create(fn.prototype);
return bound;
}
}
软绑定DEMO:
function foo() {
console.log("name:" + this.name);
}
var obj = { name: "obj" };
var obj2 = { name: "obj2" };
var obj3 = { name: "obj3" };
var fooBj = foo.softBind(obj);
fooBj(); // name: obj
obj2.foo = foo.softBind(obj2);
obj2.foo(); // name: obj2
fooBj.call(obj3); // name: obj3
setTimeout(obj2.foo, 10); // name: obj
基本类型、内置对象
基本类型:string、number、boolean、null、undefined、object
内置对象(也是内置函数): String、Number、Boolean、Object、Function、Array、Date、RegExp、Error
日常开发,大家可能会有疑问,比如var a = 'i am string'中a变量,它可以使用a.length、a.charAt(0)等属性和方法。它本来是一个字符串,为什么会有类似对象的特性呢,这里会涉及到一个基本包装对象,可以怎么理解这样的一个概念性知识,一瞬间的引用类型,用完即毁,真正的引用类型会一直存在内存中,而基本包装对象只会存在一瞬间。
衍生方法:typeof、instanceof、Object.prototype.toString.call
对象
对象的内容是由一些存储在特定命名位置的值组成的,叫做属性(指针),有两种访问方式,myObject['a']、myObject.a,对象属性同时也是无序的。
ES6可计算属性名:
const prefix = 'a';
const myObject = {
[prefix + 'bar']: "xxxx"
}
记住,函数永远不属于一个对象,只是一个引用,只是函数的this,会因为隐性绑定,在上下文做一层绑定而已。
复制对象、属性描述符
- 深复制,
JSON.parse(JSON.stringify(obj))- 浅复制,
Object.assign(...),会遍历一个或多个源对象的所有可枚举的自由键到目标对象,源对象的一些特性(如writable)不会被复制
属性描述符,即writable、configuable、enumerable、value
获得属性描述符,Object.getOwnPropertyDescriptor(myObject, 'a')
设置属性描述符,Object.defineProperty(myObject, "a", {...})
configuable配置为false,不能使用delete关键字
enumerable控制属性是否可枚举
访问描述符,(Getter/Setter),可以改写默认的value,
var myObject = {
get a() {
return 2;
}
}
myObject.a // 2
Object.defineProperty(myObject, "b", {
get: function() {
return this.a * 2 // 指向当前对象
}
});
myObject.b // 4
遍历对象属性的几种方法
for...in,遍历对象自身及原型上可枚举的属性Object.keys(),遍历对象自身可枚举的属性Object.getOwnPropertyNames,遍历对象自身的属性Object.getOwnPropertySymbol,遍历对象自身Symbol类型属性Reflect.ownkeys,遍历对象自身的属性(包含不可枚举属性,Symbol类型属性)
对象不可变性
如果你希望对象属性或是对象是不可改变,可以通过配置对象的属性描述符来实现,但是这种不可变是一种浅不可变,如果对象中属性的引用是对象、数组、函数,那么它们是可变的,实现方式有如下:
- 对象常量,
writable:false|configuable:false- 禁止扩展,
Object.preventExtensions(obj)- 密封,
Object.seal(obj)调用禁止扩展,且不能重新配置,及删除属性,及configuable:false- 冻结,
Object.freeze(obj),在密封的基础上将writable:false
存在性
- in操作符,检查属性是否存在对象里或是[[Prototype]]原型链上
Object.hasOwnProperty,只会检查对象
问题:myObject.hasOwnProperty()可能会报错,myObject可能是Object.create(null)生成不带[[Prototype]]原型链,而Object.hasOwnProperty是由[[Prototype]]委托,所以可以这样,Object.prototype.hasOwnProperty.call(myObject, "a")
有坑:
4 in [2,4,6] // false
4 in [2,2,6,8,0] // true
判断是否可枚举 myObject.propertyIsEnumerable('a')
@@iterator迭代器对象
for...of被访问的对象请求一个迭代器对象,然后通过.next()方法遍历所有返回来的值
数组内置有@@iterator,所以可以直接使用for...of。
使用内置@@iterator遍历数组:
var myArray = [1,2,3];
var it = myArray[Symbol.iterator](); // 返回迭代器函数
it.next(); // { value: 1, done: false }
it.next(); // { value: 2, done: false }
it.next(); // { value: 3, done: false }
it.next(); // { done: true }
普通对象不含有@@iterator,无法使用for...of,可以进行改造,
var myObject = { a: 2, b: 3 };
Object.defineProperty(myObject, Symbol.iterator, {
enumerable: false,
writable: false,
configuabale: false,
value: function() {
var o = this;
var idx = 0;
var ks = Object.keys(o);
return {
next: function() {
return {
value: o[ks[idx++]],
done: (idx > ks.length)
};
}
}
}
});
vat it = myObject[Symbol.iterator]();
it.next(); // { value: 2, done: false }
it.next(); // { value: 3, done: false }
it.next(); // { done: true }
你不知道的javascript(上卷)读后感(二)的更多相关文章
- 你不知道的javascript(上卷)读后感(一)
三剑客 编译,顾名思义,就是源代码执行前会经历的过程,分三个步骤, 分词/词法分析,将我们写的代码字符串分解成多个词法单元 解析/语法分析,将词法单元集合生成抽象语法树(AST) 代码生成,抽象语法树 ...
- 读《你不知道的JavaScript(上卷)》后感-作用域闭包(二)
github原文 一. 序言 最近我在读一本书:<你不知道的JavaScript>,这书分为上中卷,内容非常丰富,认真细读,能学到非常多JavaScript的知识点,希望广大的前端同胞们, ...
- 读《你不知道的JavaScript(上卷)》后感-浅谈JavaScript作用域(一)
原文 一. 序言 最近我在读一本书:<你不知道的JavaScript>,这书分为上中卷,内容非常丰富,认真细读,能学到非常多JavaScript的知识点,希望广大的前端同胞们,也入手看看这 ...
- 《你不知道的 JavaScript 上卷》 学习笔记
第一部分: 作用域和闭包 一.作用域 1. 作用域:存储变量并且查找变量的规则 2. 源代码在执行之前(编译)会经历三个步骤: 分词/此法分析:将代码字符串分解成有意义的代码块(词法单元) 解析/语法 ...
- 你不知道的JavaScript上卷笔记
你不知道的JavaScript上卷笔记 前言 You don't know JavaScript是github上一个系列文章 初看到这一标题的时候,感觉怎么老外也搞标题党,用这种冲突性比较强的题目 ...
- 你不知道的javaScript上卷(第一章 作用域是什么)
在写这篇博客时这本书我已经是看过一遍了,为了加深印象和深入学习于是打算做这系列的前端经典书籍导读博文,大家如果觉得这本书讲的好可以自己买来看看,我是比较喜欢看纸质版书的,因为这样才有读书的那种感觉. ...
- 【你不知道的javaScript 上卷 笔记3】javaScript中的声明提升表现
console.log( a ); var a = 2; 执行输出undefined a = 2; var a; console.log( a ); 执行输出2 说明:javaScript 运行时在编 ...
- JS闭包—你不知道的JavaScript上卷读书笔记(二)
关于闭包,初学者会被绕的晕头转向,在学习的路上也付出了很多精力来理解. 让我们一起来揭开闭包神秘的面纱. 闭包晦涩的定义 看过很多关于闭包的定义,很多讲的云里雾里,晦涩难懂.让不少人以为闭包是多么玄乎 ...
- JavaScript词法作用域—你不知道的JavaScript上卷读书笔记(一)
前段时间在每天往返的地铁上抽空将 <你不知道的JavaScript(上卷)>读了一遍,这本书很多部分写的很是精妙,对于接触前端时间不太久的人来说,就好像是叩开了JavaScript的另一扇 ...
随机推荐
- memcache安装与简单介绍
本文参考自菜鸟教程中的内容. 安装 安装memcache的时候,请切换为root用户 root@centos # wget http://www.memcached.org/files/memcach ...
- WPF引入OCX控件
(方法一) https://www.cnblogs.com/guaniu/archive/2013/04/07/3006445.html (方法二) 1.先注册OCX控件:(有的把OCX 控件封装到E ...
- MATLAB分类与预测算法函数
1.glmfit() 功能:构建一个广义线性回归模型. 使用格式:b=glmfit(X,y,distr),根据属性数据X以及每个记录对应的类别数据y构建一个线性回归模型,distr可取值为:binom ...
- Django后台缓存运用,提高并发
图片防盗链 -通过请求头refer控制 -nginx处理 提高网站并发的通用方法 QPS:每秒查询率QPS是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准.衡量一个服务器能抗多大并发的重要 ...
- Python 用hashlib求中文字符串的MD5值 (转自 haungrui的专栏)
使用过hashlib库的朋友想必都遇到过以下的错误吧:“Unicode-objects must be encoded before hashing”,意思是在进行md5哈希运算前,需要对数据进行编码 ...
- MySQL(六) 索引
索引与优化 1.选择索引的数据类型 MySQL支持很多数据类型,选择合适的数据类型存储数据对性能有很大的影响.通常来说,可以遵循以下一些指导原则: (1)越小的数据类型通常更好:越小的数据类型通常在磁 ...
- sql中能使用charindex 不要用 in 。charindex比in快很多
写SQL语句我们经常需要判断一个字符串中是否包含另一个字符串,但是SQL SERVER中并没有像C#提供了Contains函数,不过SQL SERVER中提供了一个叫CHAEINDX的函数,顾名思义就 ...
- QSqlDatabase
QSqlDatabase 使用静态方法addDatabase来创建一个数据库连接. 如果你的程序中只有一个数据库连接,可以使用如下语句创建连接 QSqlDatabase db = QSqlDatab ...
- Druid基本配置
最近公司要用Druid 所以看了下基本配置及配置过程中出现的问题 Druid是什么? Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0.DBCP.PROXOOL等DB池的优点,同时 ...
- springcloud断路器Dashboard监控仪表盘的使用
断路器Dashboard监控仪表盘的使用 在原有的orderserverfeignhystrix服务中使用 1.增加依赖仓库 <dependency> <g ...