前端学习笔记-js基础篇

271 阅读10分钟

原型链

  • 每个构造函数(如Person)都有一个原型对象(Person.prototype),原型有一个属性(Person.prototype.constructor)指回构造函数,而实例(如person = new Person())有一个内部指针(浏览器里是person.__proto__)指向原型。原型本身也是一个实例,相应的也有一个内部指针指向另一个原型。这样就在实例和原型之间构造了一条原型链。
  • 为了让原型链有终点,在原型链的最顶端,JavaScript规定了Object.prototype.proto === null
  • Object.getPrototypeOf(window) === window.__proto__;获取window的原型
  • Object.prototype.isPrototypeOf({})判断Object.prototype是不是{}的原型 附上自己用xmind画的原型链图

image.png

var/let/const区别

块级作用域

  • if块 if(){}
  • while块 while(){}
  • do{}while块
  • {}块
  • try{}catch{}

es6之前通过(function(){})()可以模拟创建一个块级作用域

var

  • 声明的范围是函数作用域;
  • 有变量提升;
  • 重复声明没问题如var a =1;var a =2;
  • var声明的变量,会成为全局对象winodw上的属性,而let和const声明的不会;

let

  • 声明的范围是块作用域,块作用域是函数作用域的子集;
  • 没有变量提升,有暂时性死区;
  • 重复声明会报错;

const

  • const 的行为与 let 基本相同;
  • 用它声明变量时必须同时初始化变量,如const a;会报错;
  • 修改const声明的变量会导致运行时错误;
  • 如果 const 变量引用的是一个对象,那么修改这个对象内部的属性并不违反 const 的限制,如:
const person = {}; 
person.name = 'Matt'; // ok

如果要让他有无法修改的修过,可以采用Object.freeze()冻结来实现

数组相关方法

new Array(10)和Array.of(10)

  • new Array(10)会的得到一个length为10的empty数组,如果打印值的话,会发现都是undefined。表示应该有10个值,但并没有给值。这时的数组无法用mapfilterforEach这些方法。
  • Array.of(10)相当于构建一个数组[10]

find和filter

find 和 filter 都是不改变原数组的方法

  • find只查出第一个符合条件的结果就不再继续查找,返回的是数组中的某一项,相应的findIndex返回的是这一项的索引。
  • filter查找所有符合要求的元素,返回这些元素组成的数组,一定会遍历整个数组。

Number和String

Number

  • ECMAScript可以表示的值的范围Number.MIN_VALUE~Number.MAX_VALUE,超出范围的为Infinity(正无穷)和-Infinity(负无穷)
  • Number.MAX_SAFE_INTEGER最大的安全的整数,即在这个数范围内不会出现精度丢失(小数除外),16位 Number.MAX_SAFE_INTEGER === 2**53-1
  • Number.MIN_SAFE_INTEGER最小的安全的整数
  • Number.POSITIVE_INFINITY=== Infinity
  • Number.NEGATIVE_INFINITY === -Infinity
  • 0.1+0.2 === 0.3+4e-17
  • 要确定一个值是不是有限大(即介于 JavaScript 能表示的最小值和最大值之间),可以使用 isFinite()函数

NaN

NaN === NaN//false
NaN == NaN//false

parseInt

不同的数值格式很容易混淆,因此 parseInt()也接收第二个参数,用于指定底数(进制数)

["1","2","3"].map(parseInt);//[1, NaN, NaN]

相当于

["1","2","3"].map((v, i) => parseInt(v, i));//
parseInt("1", 0);//任何数的0进制都是它本身

parseFloat()

parseFloat()函数的另一个不同之处在于,它始终忽略字符串开头的零 因为parseFloat()只解析十进制值,因此不能指定底数,只接收一个参数

Number(a)参数为对象时

对象,调用 valueOf()方法,并按照上述规则转换返回的值。如果转换结果是 NaN,则调用 toString()方法,再按照转换字符串的规则转换。

0.1+0.2不等于0.3的问题

对于这个问题,一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2,在ES6中,提供了Number.EPSILON属性,而它的值就是2,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3

String

toString()

多数情况下,toString()不接收任何参数。不过,在对数值调用这个方法时,toString()可以接收一个底数参数,即以什么底数来输出数值的字符串表示 如

let num = 10; 
console.log(num.toString()); // "10" 
console.log(num.toString(2)); // "1010"
String(Symbol('wew'))//"Symbol(wew)"
Symbol('wew')+'gty'//报错

如果字符串中包含双字节字符,那么length 属性返回的值可能不是准确的字符数

String()

函数遵循如下规则。  如果值有 toString()方法,则调用该方法(不传参数)并返回结果。  如果值是 null,返回"null"。  如果值是 undefined,返回"undefined"。

Object相关方法

===和Object.is

0 === +0;//true
0 === -0;//true
NaN === NaN;//false

Object.is(0, +0);//true;
Object.is(0, -0);//false;
Object.is(NaN, NaN);//true;

判断一个空对象

Reflect.ownKeys(obj).length === 0;

Map和Set

Set

  • 没有重复的值(+0,-0, 0)认为是一样的,两个NaN也会认为重复)
  • add(value):添加某个值,返回 Set 结构本身(可以链式调用)。
  • delete(value):删除某个值,删除成功返回 true,否则返回 false。
  • has(value):返回一个布尔值,表示该值是否为 Set 的成员。
  • clear():清除所有成员,没有返回值。
  • .size获取长度

相关遍历方法

  • keys():返回键名的遍历器。
  • values():返回键值的遍历器。
  • entries():返回键值对的遍历器。
  • forEach():使用回调函数遍历每个成员。(并没有map,filter等方法)

WeakSet

  • 成员只能是复杂数据类型,都是弱引用,容易被垃圾回收机制回收
  • WeakSet 不可迭代,因此不能被用在 for-of 等循环中。
  • WeakSet 没有 size 属性。

Map

  • 存储的是 key-value 形式的键值对,key 和 value 都可以是任何类型的,Object只能把string,number,symbol作为key
  • Map的键值对是有顺序的,迭代时顺序固定。Object的迭代顺序在不同迭代方法会有不一样
  • 除了访问(查)值,Object的性能更好,其他情况下(增,删,改)都是Map更好
  • set(key, val): 向 Map 中添加新元素
  • get(key): 通过键值查找特定的数值并返回
  • has(key): 判断 Map 对象中是否有 Key 所对应的值,有返回 true,否则返回 false
  • delete(key): 通过键值从 Map 中移除对应的数据
  • clear(): 将这个 Map 中的所有元素删除

对象转Map

for( let k of Object.keys(obj)){
  map.set(k,obj[k])
}

Map 转为对象

let obj = {}
for (let [k, v] of map) {
  obj[k] = v
}

WeakMap

  • WeakMap的键只能是复杂数据类型,都是弱引用,容易被垃圾回收机制回收
  • 不能遍历
  • 不能通过size访问长度

闭包、作用域链、执行上下文

闭包

闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。 闭包会使已经运行结束的 函数上下文 中的 变量对象 继续留在内存中,因为闭包函数保留了这个 变量对象 的 引用,可以通过它的作用域链访问到这个变量 ,所以这个变量对象不会被回收。这样会比普通的函数更占用内存

闭包的应用

  1. 实现函数柯里化
  2. 可以通过闭包的方法创建函数的私有变量,如once:只调用一次的函数
  3. 实现偏函数

作用域链

查找变量时,首先从当前上下文中的变量对象查找,如果没有就会往上查找父级作用域中的变量对象,最终找到全局上下文的变量对象,如果没有就报错。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。

执行上下文

箭头函数和this指向

箭头函数特点

  1. 箭头函数比普通函数更加简洁
  2. 箭头函数没有自己的this,箭头函数中this的指向在它在定义时已经确定了,是它所在执行上下文的this,之后不会改变。call()、apply()、bind()等方法不能改变箭头函数中this的指向
  3. 箭头函数没有prototype和this,所以不能作为构造函数使用(无法new)
  4. 箭头函数没有自己的arguments
  5. 箭头函数不能用作Generator函数,不能使用yeild关键字

this指向

箭头函数>new>bind>applycall>obj.>直接调用

具体参考文章JS 中 this 指向问题

null和undefined

  • null 作为空对象指针
  • 使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined
var a;//undefined
var b = null;//预留的存储对象

事件委托、事件冒泡、事件捕获

事件委托本质上是利用了浏览器事件冒泡的机制。因为事件在冒泡过程中会上传到父节点,并且父节点可以通过事件对象获取到目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件代理。 使用事件代理可以不必要为每一个子元素都绑定一个监听事件,这样减少了内存上的消耗。并且使用事件代理还可以实现事件的动态绑定,比如说新增了一个

事件捕获阶段。捕获指的是事件从 document 一直向下传播到目标元素,依次检查经过的节点是否绑定了事件监听函数,如果有则执行子节点,并不需要单独地为它添加一个监听事件,它所发生的事件会交给父元素中的监听函数来处理。

focus和focusin

focus:当focusable元素获得焦点时,不支持冒泡; focusin:和focus一样,只是此事件支持冒泡; blur:当focusable元素失去焦点时,不支持冒泡; focusout:和blur一样,只是此事件支持冒泡;

类型转换

装箱和拆箱

  • 装箱转换:把基本类型转换为对应的包装类型
  • 拆箱操作:把引用类型转换为基本类型

引用类型转换规则转换规则

  • 引用类型转换为Number类型,先调用valueOf,再调用toString
  • 引用类型转换为String类型,先调用toString,再调用valueOf
  • 若valueOf和toString都不存在,或者没有返回基本类型,则抛出TypeError异常。

执行+操作符时

  • 当一侧为String类型,被识别为字符串拼接,并会优先将另一侧转换为字符串类型。
  • 当一侧为Number类型,另一侧为原始类型,则将原始类型转换为Number类型。
  • 当一侧为Number类型,另一侧为引用类型,将引用类型和Number类型转换成字符串后拼接。

隐式转换表格

image.png tips:Symbol('ett')可以使用String(Symbol('ett'))和Symbol('ett').toString()转为"Symbol('ett')",但无法参数字符串和数值的加减,会报错

js的6个假值(false, "", 0, NaN, null, undefined ),其他的转为boolean都为true

对象分类

宿主对象

宿主(或内置)对象:不是由JavaScript语言本身而是由它的运行环境提供的。具体到web运用,这个环境就是浏览器。由浏览器提供的预定义对象被称为宿主对象。 Image 对象是 JS 中的宿主(或内置)对象

暂留