1.如果编写了非有效的js时,例如把return写成retrun,会抛出ReferenceError
2.使用JSON.parse()方法,可以将JSON字符串解析为JavaScript值
3.对于一个var name
如果只声明变量而未初始化,则会打印undefined。而如果用let/const
关键字声明变量且未初始化,则它被引用时会抛出ReferenceError,此情况被称为“暂时性死区”
4.在ES2020中,通过#号可以给class添加私有变量,即在class外部无法获取该值,如果尝试输出,则会抛出SyntaxError
5.SyntaxError:Javascript引擎发现了不符合语法规范的代码
ReferenceError:当对变量/项的引用被破坏或不存在时,将引发此错误.
6.在使用typeof判断null的数据类型时,会显示为object类型,这是存在的一个小问题,特殊记一下。此外typeof判断引用类型时,只能分辨出object和function两种类型
7.三种数据类型检测方法比较:
- instanceof可以准确判断复杂引用数据类型,但是不能正确判断基础数据类型
- typeof可以判断基础数据类型(null除外),但是引用数据类型中,除了function类型外,其他的也无法判断
- Object.prototype.toString(),或者Object.prototype.toString.call(xxx),最终返回[Object 数据类型],注意数据类型的首字母大写,如[Object Number]
8.数据类型转换:
a.强制类型转换 6种
- Number():对于布尔型true和false分别被转化成1和0;
数字则返回自身;null返回0;undefined返回NaN;
对于字符串,如果字符串中只包含数字则将其转换为十进制;如果字符串中包含有效的浮点格式将其转换为浮点数值;如果是空字符串将其转换成0;如果不是以上格式的字符串则返回NaN
symbol型则抛出错误 - parseInt()
- parseFloat()
- toString()
- String()
- Boolean():除了undefined,null,false,’’,NaN,0转换出来的是false,其余都是true
b.隐式类型转换 5种
- 逻辑运算符&&、||、!
- 运算符+、-、*、/
‘+’号操作符不仅可以用作数字相加,也可以用于字符串拼接
如果其中有一个是字符串,另外一个是undefined、null或布尔型,则调用toString()方法进行字符串拼接
如果其中有一个是数字,另外一个是undefined、null、布尔型或数字,则会将其转化成数字进行加法运算
如果其中一个是字符串,一个是数字,则会按照字符串规则进行拼接
如果**‘+’两边都为字符串**,则将得到经过加法运算后的字符串 - 关系运算符>、<、>=、<=
- 相等运算符==:
如果类型相同,无需进行类型转换;
如果其中一个操作值为null或者undefined,那么另外一个操作值必须为null或者undefined才会返回true,否则都返回false;
如果其中一个是symbol类型,那么返回false;
两个操作值都为string和number类型,那么就会把字符串类型转换为number;
如果一个操作值为boolean,那么转换为number
如果一个操作值为object,且另一方为string、number、或者symbol,就会把object转换成原始类型再进行判断 - if/while条件
9.object转换规则
- 如果部署了
[symbol.toPrimitive]()
方法,优先调用再返回 - 调用valueOf(),如果转换为基础类型则返回
- 调用toString(),如果转换为基础类型则返回
- 如果都没有返回基础类型,会报错
10.JS中实现浅拷贝的方法:4种
浅拷贝即只能拷贝一层对象,如果出现对象的嵌套,则无能为力
- object.assign:object.assign是ES6中object的一个方法,该方法可以用于JS对象的合并等多个用途,其中一个用途就是可以浅拷贝。语法为
object.assign(target,...sources)
示例代码:
let target = {}
let source = {a:{b:2}}
object.assign(target,source) // 拷贝的是引用数据类型,即拷贝的是地址
console.log(target) // {a:b:10}
source.a.b = 10
console.log(source) // {a:b:10}
console.log(target) // {a:b:10}
- 扩展运算符方法:利用JS扩展运算符,在构造对象的同时完成浅拷贝的方法。语法为
let cloneObj = {...obj}
示例代码:
let obj = {a:1,b:{c:1}}
let obj2 = {...obj}
obj.a = 2
console.log(obj)// {a:2,b:{c:1}}
console.log(obj2)// {a:1,b:{c:1}}
obj.b.c = 2
console.log(obj)// {a:2,b:{c:2}}
console.log(obj2)// {a:1,b:{c:2}}
- concat拷贝数组:只能用于数组的拷贝
示例代码:
let arr = [1,2,3]
let newArr = arr.concat()
newArr[1] = 100
console.log(arr)// [1,2,3]
console.log(newArr)// [1,100,3]
- slice拷贝数组:仅针对数组对象,语法为
arr.slice(begin,end)
,返回一个新的数组对象,不会更改原数组
11.手工实现一个浅拷贝
- 对基础类型做一个最基本的浅拷贝
- 对引用类型开辟一个新的存储,并且拷贝一层对象属性
代码示例:
const shallowClone = (target) => {
if(typeof target === 'object' && target !== null){
const cloneTarget = Array.isArray(target)?[]:{}
for(let prop in target){
if(target.hasOwnProperty(prop)){
cloneTarget[prop] = target[prop]
}
}
return cloneTarget
}
else{
return target
}
}
12.JS中实现深拷贝的方法:
深拷贝的原理为:将一个对象从内存中完整的拷贝出来一份给目标对象,并从堆内存中开辟一个全新的空间存放新对象,且新对象的修改并不会改变原对象,二者实现真正的分离
- 乞丐版(JSON.stringify):是目前开发过程中最简单的深拷贝方法。
示例代码:
let obj = {a:1,b:[1,2,3]}
let str = JSON.stringify(obj)
let obj2 = JSON.parse(str)
console.log(obj2) // {a:1,b:[1,2,3]}
obj.a = 2
obj.b.push(4)
console.log(obj) // {a:2,b:[1,2,3,4]}
console.log(obj2) // {a:1,b:[1,2,3]}
JSON方法中存在的缺陷:
1.拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,经过 JSON.stringify 序列化之后的字符串中这个键值对会消失;
2.拷贝 Date 引用类型会变成字符串;
3.无法拷贝不可枚举的属性;
4.无法拷贝对象的原型链;
5.拷贝 RegExp 引用类型会变成空对象;
6.对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的结果会变成 null;
7.无法拷贝对象的循环应用,即对象成环 (obj[key] = obj)。
- 改进版,通过递归实现
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
const deepClone = function (obj, hash = new WeakMap()) {
if (obj.constructor === Date)
return new Date(obj) // 日期对象直接返回一个新的日期对象
if (obj.constructor === RegExp)
return new RegExp(obj) //正则对象直接返回一个新的正则对象
//如果循环引用了就用 weakMap 来解决
if (hash.has(obj)) return hash.get(obj)
let allDesc = Object.getOwnPropertyDescriptors(obj)
//遍历传入参数所有键的特性
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
//继承原型链
hash.set(obj, cloneObj)
for (let key of Reflect.ownKeys(obj)) {
cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
}
return cloneObj
}
13.继承:继承可以使子类别具有父类的各种属性和方法
当前一共有6种继承方法:
- 原型链继承:缺点为共享问题。它们的内存空间是共享的,当一个发生变化的时候,另外一个也随之进行了变化。
- 构造函数继承(借助call):优化了原型链继承的共享问题,但是其只能继承父类的实例属性和方法,不能继承原型属性或者方法。
- 组合继承(前两组结合):这里涉及多次构造,增加性能开销