最近粗略读了一下《你不知道的JavaScript》这本书,以下是自己觉得可能会遗忘并且有必要记住的一些知识点,写下这篇文章记录一下
上卷
3.函数作用域和块作用域
匿名函数缺点
1、在栈追踪中不会显示出有意义的函数名。使得调试困难
2、没有函数名,难以引用
3、可读性,可理解性差
4、提升
4.3函数优先
foo() // 1 function foo函数先提升 var foo重复申明被忽略
var foo
function foo(){
console.log(1)
}
var foo = function(){
console.log(2)
}
5、作用域闭包(for循环保留每次迭代下来的i )
5.2
当函数可以记住并访问所在的词法作用域时,就产生了闭包
5.3
只要使用了回调函数,实际上就是在使用闭包
第二部分
2.2.2.1
严格模式下无法使用默认绑定
function foo(){
"use strict"
console.log(this.a)
}
var a = 2
foo() // TypeError: this is undefined
虽然this的绑定规则完全取决于调用位置,但是只有在非strict mode下时默认才到全局对象,
严格模式下与foo调用位置无关
function foo(){
console.log(this.a)
}
var a = 2
(function(){
"use strict";
foo() // 2
})()
2.2.2隐式丢失
如下
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo:foo
}
var bar = obj.foo
var a = 'global_a'
bar() // global_a
bind call等硬绑定方式可以解决
var bar = obj.foo.bind(obj)
var bar = ()=>{obj.foo.call(obj)}
小点:forEach的第二个参数即为this指向
2.2.4 new绑定
new调用时执行如下操作
- 创建一个全新对象
- 这个新对象会被执行原型连接
- 修改this指向
- 返回这个对象
2.4.3 软绑定
function foo(){
console.log(this.name);
}
var obj1 = {name:'obj1'},obj2 = {name:'obj2'};
let fooObj = foo.bind(obj1);
fooObj(); // obj1
fooObj.call(obj2); // obj1
硬绑定之后无法改变this 原因fooObj相当于(简易,省去了一些步骤)
fooObj = ()=>{
let arg = [].prototype.slice(arguments,1)
return foo.apply(obj1,arg)
}
所以再在外部call也没有
原理
检测调用时的this 若this是全局或者undefined则默认指定对象,否则不修改this
第三部分
3.3.5 属性描述符
Object.getOwnPropertyDescriptor(myObject,'a')
3.3.6 不变性
方法 | 代码 | 解释 |
---|---|---|
禁止扩展 | Object.preventExtensions(myobject) | 无法拓展新属性 |
密封 | Object.seal() | 无法添加新属性也不能重新配置或删除任何现有属性 相当于现有对象都调用preventExtensions并把所有属性标记为configurable:false |
冻结 | Object.freeze() | 相当于现有对象上调用seal并都标记上writable:false |
3.3.10 存在性
let myObj = {a:2}
(a in myObj) // 是否存在对象及prototype原型链上
myObj.hasOwnProperty('a') // 是否存在对象myObj上
myObj.propertyIsEnumberable('a') // 是否可枚举
x手动查看iterator
var myArray = [1,2,3];
var it = myArray[Symbol.iterator]()
it.next();
it.next();
it.next();
it.next();
es实现 P123
Object.defineProperty(myObject,Symbol.iterator,{
writable:false,
enumberable:false,
configurable:true.
value(){
const o = this;
let idx = 0;
let ks = Object.keys(o);
return {
next(){
return {
value:o[ks[idx++]],
done:(idx>ks.length)
}
}
}
}
})
类
继承
第五章 原型
5.1.2 属性设置和屏蔽
设置myObject.foo = 'bar'的三种情况
- 原型链上有foo且未被标记为只读,则会为myObject直接添加一个foo属性
- 原型链上存在foo但被标为只读,则无法修改已有属性或创建,严格模式下抛出错误
- 若原型链上层存在foo并且是一个setter,则会调用,即不会改变已有也不会新创建
5.3 原型关联
会有缺陷的方式
Bar.prototype = Foo.prototype //直接引用就会导致同时修改
Bar.prototype = new Foo() //若Foo有副作用,会影响bar后代,如写日志修改状态等
正确方式
Bar.prototype = Object.create(Foo.prototype)
Object.create的polyfill
if(!Object.create){
Object.create=function(o){
function F()
F.prototype = o
return new F()
}
}
中卷(第一部分)
第二章
2.3.1数字的语法(toFixed)
let a = 42.59
a.toFixed(1) // 42.6 四舍五入
42.toFixed(3) //SyntaxError
(42).toFixed(3)
0.42. toFixed(3)
42..toFixed(3)
2.3.2比较数值的大小
0.41+0.2===0.3;//false 二进制浮点数不是十分精准
设置一个误差范围 通常称为 机器精度
if(!Number.EPSILON){
Number.EPSILON = Math.pow(2,-52)
}
// 判断两数是否相等
function numberIsEqual(){
return Math.abs(n1-n2)<Number.EPSILON;
}
2.3.4 整数检测
Number.isInteger(42) // true
Number.isInteger(42.000) // true
// polyfill
if(!Number.isInteger){
Number.isInteger = function(num){
return typeof num == 'number' && num%1 == 0
}
}
2.4.3 isNaN有严格缺陷
var a = 2/'foo'
var b = 'foo'
window.isNaN(a) //true
window.isNam(b) //true
Number.isNaN的polyfill
if(!Number.isNaN){
Number.isNaN = function(n){
return n!=n
}
}
2.4..4 特殊等式()
Object.is()
if(!Object.is){
Object.is = function(v1,v2){
if(v1===0&&v2===0){
return 1 / v1 === 1/v2 //+-0的判断
}
if(v1 !== v1){
return v2 !== v2 //NaN的判断
}
return v1 === v2
}
}
第四章
4.2.1 toString
var a = 1.07*Math.pow(10,21)
a.toString() //1.07e21
stringify
1、若对象中有toJSON方法则会使用该方法并将其返回值序列化
let obj = {
name:'test',
age:'18',
toJSON(){
return {result:'this is result'}
}
}
console.log(JSON.stringify(obj))
2、该方法有第二个参数
- 数组 :表示要被序列化的属性
- 函数:对对象本身调用一次,接着对对象中的每个属性各调用一次,每次传两个参数 建和值,要忽略某个键就返回undefined
let a = {a:'valuea',c:'valuec',d:'valued'}
JSON.stringify(a,['b','c']);
JSON.stringify(a,function(k,v){
if(k!=='c') return v
})
3、第三个参数表示缩进,
- 数字表示每一级缩进的字符数
- 字符串最前面的是个字符被用于每一级的缩进
let a = {a:'valuea',c:'valuec',d:'valued'};
JSON.stringify(a,null,3);
JSON.stringify(a,null,"------")
4.2.2 toNumber
undefined -> NaN
null -> 0
toNumber原理(toPrimitive(y))
- 判断是否有valueOf()方法,若有且返回基本类型值就使用该值进行强制类型转换
- 若没有就用toString()的返回值来进行强制类型转换
- 若valueOf()和toString()均不返回基本类型值就会产生TypeError错误
4.3.1
日期显示转换为数字
利用+ 将Date对象强制转化为数字 返回时间戳
let d = new Date('Tue Jul 13 2021 22:42:03 GMT+0800 ')
console.log(+d)
一般不建议上述用法
可以使用Data.now获取当前时间,使用new Date.getTime()获取指定时间的时间戳
4.3.2 显示解析数字字符串
parseInt 解析 允许出现非数字
Number 转换 不允许出现非数字
var a = '42px'
Number(a) // NaN
parseInt(a) // 42
parseInt先转化为字符串再解析
var a = {
num:21,
toString(){
return String(this.num*2)
}
}
parseInt(a) // 42
parseInt第二个参数
4.4.2隐式强制类型转换
a+‘’
var a = {
valueOf(){
return 42;
},
toString(){
return 4
}
}
a + ''; //'42'
String(a); //'4'
[3]-[2]
var a = [3];
var b = [1];
a - b; // 2
先调用toString()然后再转化为数字
==
42 == '42' // 是字符串转为数字 true
‘42’ == true //同样转为数字 false
null == undefined // true
[1,2,3] == '1,2,3' //true [1,2,3]->'1,2,3'
对象与非对象之间的转换
对象通过上方的toPrimitive转换为字符串或数字
var a = 'abc'
var b = Object(a)
a == b //true
var a = null
var b = Object(a) //和Object()一样
a == b //false
var a = undefined
var b = Object(a) //和object()一样
a == b //false
var a = NaN //同上
== 和 === 使用场景
若两边中的值有true或false,不使用 ==
有[],"",0不要使用==
第五章
try catch(P118)
若finally中抛出异常 则函数会在此终止,若此前有返回值,该值作废
finally中的reeturn会覆盖try和catch中的return
function foo(){
try{
return 42
}
finally{
throw 'Oops!'
}
console.log('nerver runs')
}
console.log(foo()) //error Uncaught Oops! (line 11)
中卷(第二部分)
第一章
1.4.3协作
ajax请求回来后的n条数据进行分批请求
let res = []
function response(data){
var chunk = data.splice(0,1000);
res = res.concat(chunk.map(value=>value*2))
if(data.length>0){
setTimeout(()=>{
response(data)
},0)
}
}
第二章 省点回调
2.4 超时处理
请求超时的一些处理
function timeoutify(fn,delay){
let timmer = setTimeout(()=>{
timmer = null
fn(new Error('timeOut'))
},dealy)
return function(){
if(timmer){
clearTimeout(timmer)
fn.call(this,arguments)
}
}
}
foo(err,data){
if(err){
console.error(err)
}else{
console.log(data)
}
}
ajax('http://.....',timeoutify(foo,500))
第三章 Promise
3.3.2 调用过晚(注意Promise的展开)
resolve中若是个Promise则会将其异步展开(可以简单的理解为里面的promise替换了外部的),reject则直接返回得到的值
var p3 = new Promise((resolve,reject)=>{
resolve('B')
})
var p1 = new Promise((resolve,reject)=>{
resolve(p3)
})
var p2 = new Promise((resolve,reject)=>{
resolve('A')
})
p1.then(value=>{
console.log(value)
})
p2.then(value=>{
console.log(value)
})
// B A
promise.race可用于超时操作
第四章 生成器
4.1.1
一般来说需要的next比yield多一个,简单点理解:
function *fn(){
let a = 1;
yield 10;
a += (yield)
console.log(a)
}
let test = fn()
test.next()
test.next()
test.next(10)
以上代码 简单点说
- 第一次next运行到第一个yield,yield后面的值,即为返回值(done:false,value:10)
- 第二次next运行到第二个yield,yield后面没有值,即返回undefined(done:false,value:undefined)
- 第三次next运行完整个项目,yield后没有值 即返回undefined(done:true,value:undefined)
4.2.1 为生成器实现迭代器接口
for..of执行的原理
let something = (function(){
var nextVal=108;
return {
// for..of循环所需要的属性
[Symbol.iterator]:function(){return this},
next(){
if(nextVal === undefined){
nextVal = 1
}else{
nextVal = (3 * nextVal) + 6
}
return {done:false,value:nextVal}
}
}
})()
for(let item of something){
if(item < 100){
console.log(item)
}
}
// 在数组中
let arr = [1,2,3,4,5]
let it = arr[Symbol.iterator]();
console.log(it.next())
执行一个生成器也能得到一个迭代器
function *scq(){
let nextVal
while(true){
if(nextVal === undefined){
nextVal = 10
}else{
nextVal += 10
}
yield nextVal
}
}
for(let item of scq()){
if(item < 100){
console.log(item)
}
}
使用外部return可以手工终止
function *scq(){
try{
let nextVal
while(true){
if(nextVal === undefined){
nextVal = 10
}else{
nextVal += 10
}
yield nextVal
}
}
finally{
console.log('cleaning up!')
}
}
let it = scq()
for(let item of it){
if(item < 100){
console.log(item)
}else{
console.log(it.return('all over'))
}
}
//输出结果
// 。。。。。一串数字
//"cleaning up!"
//[object Object] {
// done: true,
// value: "all over"
//}
下卷
2.4.1对象属性赋值模式
let a = {x:12,y:14}
let {x,y} = a //等价于{x:x,y:y} = a
以上省略的时 x:x中的 x:
也就是说其中{x:x
中前面的部分x:
时固定写死,后面是可以灵活变动的
如下
let a = {x:12,y:14};
let {x:abc,y:def} = a
console.log(abc); // 12
console.log(def); // 14
2.5.1设置默认值
let arr = [1,2,3]
let [arra=11,arrb=12,arrc=13,arrd=14] =arr
console.log(arra,arrb,arrc,arrd)
let obj={a:21,b:22}
let {a:obja=31,b:objb=32,c:objc=33} = obj
console.log(obja,objb,objc)
2.5.3结构参数
defaultConfig为默认设置,btn自己设置的个例
现在想做的操作是将将defaultConfig中的设置给到btn但不改变btn已有的设置
方案一
let defaultConfig = {
options:{
click:true,
input:true,
},
css:{
color:'white',
background:'blue'
}
}
let btn = {
options:{
click:true,
input:false
}
}
btn = Object.assign({},defaultConfig,btn)
console.log(btn)
但是该方式为浅拷贝,改变btn中的属性的时候会使得默认配置中的属性也发生改变
方案二
let defaultConfig = {
options:{
click:true,
input:true,
},
css:{
color:'white',
background:'blue'
}
}
let btn = {
options:{
click:true,
input:false
}
}
// btn = Object.assign({},defaultConfig,btn)
// console.log(btn)
{
let {
// options默认值设为空,当btn有该值时用btn中的值
options:{
// 默认设置click为defautlConfig中的值,btn.config中有对应值时使用对应值
click=defaultConfig.options.click,
input=defaultConfig.options.input,
}={},
css:{
color=defaultConfig.css.color,
background=defaultConfig.css.background
}={}
} = btn
// 上方暴露的时{click...,input..},这点可参考以下表达式
// let {a:x} = {a:18} console.log(x)
btn = {
options:{click,input},
css:{color,background}
}
}
console.log(btn)
2.10.3 正则表达式flags
查看正则表达式的内容和标识
- es6规定了表达式按这个顺序输出 gimuy
let re = /foo/ig
re.flags; //gi
re.source //foo
3.4.2 extends和super
P173拓展原生类
class MyCollArray extends Array{
firstItem(){
return this[0]
}
lastItem(){
return this[this.length-1]
}
}
let arr = new MyCollArray('a','b','c')
console.log(arr.firstItem()); //a
console.log(arr.lastItem()); //c
'foo'.repeat(3) //'foofoofoo'