原型链
- 每个构造函数(如
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画的原型链图
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个值,但并没有给值。这时的数组无法用map
、filter
、forEach
这些方法。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访问长度
闭包、作用域链、执行上下文
闭包
闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。 闭包会使已经运行结束的 函数上下文 中的 变量对象 继续留在内存中,因为闭包函数保留了这个 变量对象 的 引用,可以通过它的作用域链访问到这个变量 ,所以这个变量对象不会被回收。这样会比普通的函数更占用内存
闭包的应用
- 实现函数柯里化
- 可以通过闭包的方法创建函数的私有变量,如once:只调用一次的函数
- 实现偏函数
作用域链
查找变量时,首先从当前上下文中的变量对象查找,如果没有就会往上查找父级作用域中的变量对象,最终找到全局上下文的变量对象,如果没有就报错。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。
执行上下文
- 执行上下文也可以叫执行环境,this可以理解为是执行上下文的一个属性
- 全局上下文、函数上下文、eval()上下文 详细内容参考木易杨前端进阶文章理解JavaScript 中的执行上下文和执行栈
箭头函数和this指向
箭头函数特点
- 箭头函数比普通函数更加简洁
- 箭头函数没有自己的this,箭头函数中this的指向在它在定义时已经确定了,是它所在执行上下文的this,之后不会改变。call()、apply()、bind()等方法不能改变箭头函数中this的指向
- 箭头函数没有prototype和this,所以不能作为构造函数使用(无法new)
- 箭头函数没有自己的arguments
- 箭头函数不能用作Generator函数,不能使用yeild关键字
this指向
箭头函数>new
>bind
>apply
和call
>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类型转换成字符串后拼接。
隐式转换表格
tips:Symbol('ett')可以使用String(Symbol('ett'))和Symbol('ett').toString()转为"Symbol('ett')",但无法参数字符串和数值的加减,会报错
js的6个假值(false
, ""
, 0
, NaN
, null
, undefined
),其他的转为boolean都为true
对象分类
宿主对象
宿主(或内置)对象:不是由JavaScript语言本身而是由它的运行环境提供的。具体到web运用,这个环境就是浏览器。由浏览器提供的预定义对象被称为宿主对象。 Image 对象是 JS 中的宿主(或内置)对象
暂留