你不知道的JavaScript个人笔记

417 阅读8分钟

最近粗略读了一下《你不知道的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' // 是字符串转为数字 true42’ == 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 //同上

== 和 === 使用场景

若两边中的值有truefalse,不使用 ==
有[],"",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'