看了一些资料,结合ES6、高程和MDN,对JS核心知识点进行了梳理。由于篇幅有限,这里只对我认为重要的知识做了介绍。一些常识性的东西可以参考高程,另外一些核心知识点的扩展可以参考我其他的文章。本文适合作JS知识点复习/突击用,亦可作为前端面试指导。
数据类型(8种)
基础类型
存储到栈内存中,操作的是值
- null:空指针,所以typeof null ==>Object(底层二进制码全部为0,而js固定如果前三个为0就是object)
- undefined:定义了未赋值
- Number:数字
- String:字符串
- Symbol:一种实例是唯一且不可改变的数据类型。
- Boolean:布尔值
- bigInt
引用类型
储存在堆内存种,操作的是空间地址
Object:具体可以是Array,Function,RegExp,Date
栈内存和堆内存
为了更好的理解栈内存和堆内存,给一张图
栈内存就是线性表,因为基础类型简单,内存固定,所以给个线性表就能存
堆内存是啥,这个堆和树结构堆没啥关系,你可以把它理解成一大堆(坨?) 里面的东西内存大小并不固定
声明变量
ES5 只有两种声明变量的方法:var命令和function命令。ES6 除了添加let和const命令,后面章节还会提到,另外两种声明变量的方法:import命令和class命令。所以,ES6 一共有 6 种声明变量的方法。 --阮一峰es6
var
特点
:变量提升,没有块级作用域
说到var肯定要提变量提升:当前作用域,js(函数)执行前,浏览器会把带var或者function进行提前声明和定义
- 变量只声明,函数是声明+赋值,自执行函数定义和执行一起完成了
- 不受逻辑判断条件影响
- return 下面的也提升,但是return 里面的不提升
重复的声明可以,重新赋值即可,但是变量和方法名字不能冲突
const
特点
:常量,地址不变,但是属性可以变
当我们定义一个const对象的时候,我们说的常量其实是指针,就是const对象对应的堆内存指向是不变的,但是堆内存中的数据本身的大小或者属性是可变的。而对于const定义的基础变量而言,这个值就相当于const对象的指针,是不可变。
既然知道了const在内存中的存储,那么const、let定义的变量不能二次定义的流程也就比较容易猜出来了,每次使用const或者let去初始化一个变量的时候,会首先遍历当前的内存栈,看看有没有重名变量,有的话就返回错误。
let:
特点
:块作用域,暂时性死区(TDZ),不进行变量提升,不允许重复声明
//只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。所以下面代码不报错,外层作用域和里层作用域都有一个tmp
let tmp = 123;
if (true) {
let tmp =123;
}
//ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
let tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
import:es6模块化解决方案
class:es6继承解决方案
判断数据类型
typeof
它是操作符
用来判断类型
优点:判断基础类型很快
缺点:无法准确判断引用类型
typeof '' // ==> string
typeof 1 //==> number
typeof true //==>boolean
typeof undefined //==>undefined
let b = Symbol() ; typeof b //==>symbol
-----------------下面的判断不了实际类型了-----------------------
typeof function fn(){} //==>function
typeof null //==>object
typeof [1,2,3] //==>object
typeof {} //==>object
instanceof
根据xxx是否出现在原型链中来判断是否是xxx的实例
优点:正常使用判断实例还是挺快的
缺点:1、风险来源于原型链的改写,2、会不断沿原型链查找,所以会有多个构造函数
//缺点2的一个演示
let arr = [1,2,3]
arr instanceof Array //==>true
arr instanceof Object //==>true
----------------------------
//缺点1的一个演示
//强行改变原型影响instanceof的判断
var a = {}
a instanceof Function
false
a.__proto__ = Function
ƒ Function() { [native code] }
a instanceof Function
true
instanceof操作符的问题在于,它只有一个全局执行环境,如果网页有多个框架,那实际上就存在两个以上的不同的全局执行环境,从而存在两个以上不同版本的Array构造函数。如果从一个框架想另外一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具备各自不同的构造函数 ----高程page88 (笔者白话问翻译一下:风险来至原型链的重写
)
constructor
根据继承的原理去判断类型
优点:只能判断一层
缺点:风险同样来之于原型链的重写,比如当你在多个frame中来回穿梭的时候,这两种方法就亚历山大了。由于每个iframe都有一套自己的执行环境,跨frame实例化的对象彼此是不共享原型链的,因此导致上述检测代码失效!
[].constructor === Array //true
[].constructor.constructor === Function // true
[].constructor === Object //false
isNaN:
判断是否是NAN这个方法会先调用Number
,所以不是很好用
console.log(isNaN("1px")); //true
//先调用Number('1px'),返回NaN,
//然后再调用isNaN(NaN)返回true
//燃鹅 '1px'客观并不是NaN
Object.prototype.toString.call()
Object.prototype.toString.call(null) // ==> [object Null]
Object.prototype.toString.call([]) // ==> [object Array]
isArray
Array.isArray([]) //==>true
Object.is
优点判断 null NaN比较方便 不是那么hack
let a = null
Object.is(a , null) //==>true
Object.is(NaN , NaN) //==>true
类型转化
这一块内容太多,太杂了,其实我不怎么想写,因为很少有人会这么写代码。但是这块太重要了,面试必考。建议大家掌握这块的核心内容以及原则,不要关注奇淫巧技。
自动装包
三种包装类型:Number,Boolean,String
let s1 = '123'
let s2 = s1.slice(2) // a是基本类型,它是没有slice方法的这里实际上后台完成了一个自动装包
---下面是实际发生的事---------
let s1 = new string('123')
let s2 = s1.slice(2)
s1 = null //注意这里用完就销毁了
//所以如果添加某个属性后面是调用不出来的
let s1 = '123'
s1.color = 'red'
console.log(s1.color) // ==> undefind
//这里特别说一下 为什么123.toString会报错 //Uncaught SyntaxError: Invalid or unexpected token
//这个是语法错误,因为编译的时候没有办法判断是小数点还是调用方法
要么用括号包起来 要么用..
这些类型(构造函数)基本都重写了它们的tostring方法
Number
Number :将其他数据类型的值强制转换成number类型;
Number(true) // 1
Number(false) // 0
Number({}) //NaN
Number(undefined) // NaN
Number(null) // 0
Number([]); // 0
Number([1]); // 1
Number([1,2]); // NaN
Number(['1px']); // NaN
Number("") // 0
Number("1") // 1
Number("1px") // NaN
???这么复杂 ???
没办法,背也得背下来 。因为很多操作符、方法会自动装包,自动调用Number,所以不掌握这块很容易出bug
和number相关的方法及运算符
1.parseInt
全局方法,经常用于字符串提取数字的方法; 把字符串中从左到右依次识别,直到遇到一个非有效数字,停止,把找到的数字返回;
parseInt("12px12") // 12
parseInt("12.666.777px12") // 12
parseInt("px12.666px12") // NaN
parseInt("") // NaN
parseInt(true) // NaN
parseInt({}) // NaN
parseInt([]) // NaN
parseInt(null) // NaN
parseInt(undefined) // NaN
2.toFixed : 保留小数点位数的方法,返回值是一个字符串
;
3.位运算
// 转整数
~~1.2 //1
~~'1.2' //1
~~'1.2px' //0 先调用Number()转换成数字,再调用~~转换成整数 ~~NaN 0
4.-转化
会先把字符串转换成数字(Number
),然后再进行计算,注意NaN,undifined参与的任何计算都是NaN
console.log("6" - 2);//==> 4
console.log("5px"-"4")//==> NaN (NaN-4还是NaN)
// * 号类似
1*‘1.1’ //1.1
1*'1.1px' //NaN
5.+转化
具体调用string还是number请看下表
遇到string或者obj转成string就行了
|| undefined | null | boolean | number | string | object |
=========================================================================
undefined || number | number | number | number | string | string |
null || number | number | number | number | string | string |
boolean || number | number | number | number | string | string |
number || number | number | number | number | string | string |
string || string | string | string | string | string | string |
object || string | string | string | string | string | string |
//字符串、和任何类型相加都是调用String
var a = typeof 10 + true + [] + null + undefined+{};
console.log(a); //==>numbertruenullundefined[object Object],{}
console.log("6px"+undefined); ==> 6pxundefined
console.log(NaN+"undefined");==> NaNundefined
//经典面试题
[1,2]+[2,1] //==>都调用toString '1,2'+'2,1'===>'1,22,1'
console.log({name:'fyy'}+'11')
[object Object]11
/*腾讯面试题*/
let result = 1+null+true+undefined+'Tencent'+false+[]+undefined+null;
console.log(result); //NaNTencentfalseundefinednull
6.布尔值Boolean
其他数据类型转布尔类型是false有且只有五个值: 0 "" NaN null undefined
所以boolean({}) 或者boolean([]) 都是真
==和===
===是全等,==是类型转化后再判断,规则比较复杂。这里我认为除了准备面试需要看看,平时基本不会用,所以这个知识性价比非常低,学了用不到也会忘,大家自己把握,详细规则可以搜我其他文章
平时除了判断a是否是undefined或者是null(jq源码里面都用法
)都时候其他情况下都用===
console.log(null==undefined) // true
console.log(undefined==undefined) // true
这里再补充一个,判断数字和字符串相等的以后用==也非常好用。比如后台返回的数据一会是1一会儿是‘1’你需要封装一个通用方法的时候,可以使用==来进行判断
还是说一下吧
对象==对象 比较地址
对象==字符串 对象变字符串再比较
NaN==NaN false
null==undefined true
其他情况 只要两边不同 全部转换为数字
console.log([]==false); 对象==布尔 全部转化为数字 0==0 true
console.log(![]==false); ![]先运算 false==false 全部转化为数字 true
总结
本期文章估计大家看的很烦,无奈,我写起来也非常枯燥。因为不仅得讲规则,还得写用例。还是那句话,建议大家掌握核心知识点,细枝末节的东西就随意啦。这一期文章还有许多东西可以展开来讲,篇幅有限,我将会在后面的文章中一一涉及。感谢大家的阅读,本人水平有限,如果有不对的地方请在评论区指出。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。