作为一名前端,javaScript是作为日常的使用必备。在网上也看到很多人对于javaScript的一些总结,总是感觉写的有点云里雾里不太详细,别人消化了的东西不一定适合自己。这边将一些javaScript的重要知识点进行总结。(适合初学者观看)
1.变量定义:var, let, const的区别
区分这几个变量主要通过作用域,变量提升,是否可改变来进行区分
作用域: var -> 全局作用域 let,const -> 块级作用域
作用域可分为:全局作用域,块级作用域,函数作用域,作用域的区别区分变量在不同地方的作用范围
变量提升:var -> 有变量提升, let, const -> 无变量提升
console.log(a) // undefined
var a = 1;
console.log(b) // 报错
let b = 1;
console.log(c) // 报错
const c = 1;
是否可重新赋值改变: var,let -> 可以 const -> 不可以
var a = 1; a= 2;
let b = 1; b = 2;
const c = 1; c = 2; //报错
2.数据类型
javaScript支持6种基本数据类型(number, string, boolean, null, undefined, symbol)和一种引用类型(object)
如何判断不同的数据类型?
1.通过typeof判断
typeof 1 // 'number'
typeof 'str' // 'string'
typeof true // 'boolean'
typeof null // 'object'
typeof undefined // 'undefined'
typeof Symbol(1) // 'symbol'
typeof {} typeof [] // ‘object'
通过以上代码可以看出来typeof的优缺点,优点:可以对基本数据类型进行判断, 缺陷:对于null和引用数据类型无法进行具体的判断
2.通过instance 进行判断
let a = new Number(1); a instanceof Number // true
1 instanceof Number // false
// 同理对于其他类型进行判断 (null, undefined除外)
let arr = []; arr instanceof Array // true
let obj = {}; obj instanceof Object // true
通过以上代码可以看出instanceof的优缺点以及原理,原理是看被判断值的原型链上是否有判断值类型(实例的原型上是否出现相应的构造函数)
优点:可以对具体的引用值类型进行判断,缺点: 不能判断基本类型
3.通过Object.prototype.toString.call()判断
Object.prototype.toString.call(1) // '[object Number]'
Object.prototype.toString.call([]) // '[object Array]'
通过以上代码可以看出来,该方式对于基本的判断都能实现
优点:对于基本值类型以及引用值类型都能进行判断, 缺点:太具体了,必须确切到某个类型,代码太长哈哈
tips: undefined与null有什么区别? undefined是声明但未赋值,null是空引用
3.循环语句(for)
for: 循环遍历可迭代对象
for…in: 遍历对象
const obj = {name: 'mll', age: 23, sex: '女'}
for(let key in obj) {
console.log(key) // name age sex
}
for…of: 循环遍历可迭代对象
const arr = [1,2,3]
for (const item of arr) {
console.log(item) // 1 2 3
}
break和continue: break用于跳出循环, continue用于跳出该次循环
qs: map和forEach无法通过break跳出循环? answer: 改使用for循环
4.原始值与引用值
原始值类型在内存中是以栈形式存储, 引用值类型在内存中是以堆形式存储。
原始值类型赋值后,是在内存中开辟了一块新内存;引用值类型赋值后,是将值的引用赋值给其他元素,本质上两个值还是指向同一个内存地址,当一个值改变时,另一个值也会进行变动;
知识点:深浅拷贝
浅拷贝:浅拷贝只能拷贝基本类型的数据,对于引用类型的数据拷贝的是引用
Object.assign()
递归遍历 (只遍历一层)
…运算符
深拷贝: 深拷贝在内容中新开辟一个地址用于存储被拷贝对象
JSON.stringify() 和JSON.parse() // 缺点不能支持循环引用, undefined和function会丢失
手写递归实现(代码如下)
// source->要拷贝的对象
// map->键值对的对应关系
function deepClone(source, map = new WeakMap()) {
// 基本类型,直接拷贝返回
if (typeof source !== 'object' || source === null || source === undefined) {return source}
let result = new source.constructor();
let mapRes = map.get(source);
if (mapRes) {
return mapRes
}
map.set(source, result)
for (const key in source) {
result[key] = deepClone(source[key], map)
}
return result
}
内存清理:对于在使用中不用的循环引用情况,及时的置Null清空内存
5.Map和Set
Map: map的一种键值对类型,键不同于对象的只能是string类型,可以为任意类型
Set: Set集合可以自动过滤去除相同元素的数组格式
const test1 = [1,1,2,3]
const newArr1 = [...new Set(test1)] // [1, 2, 3]
const arr = [{name: 'mll'}, {name: 'mll'}, {age: 12}]
const newArr = [...new Set(arr)] // [{name: 'mll'}, {name: 'mll'}, {age: 12}] --Attention
6.继承实现方式(6种)
对于继承的实现方式是一些必备的知识,可能是我接触的项目不太使用得到,但确实是一个重要得知识点。
1.构造函数继承
因为原型链继承的引用属性会在多实例间共享以及不能向父类进行传参,构造函数的继承便可以解决相关问题
实现:
function Father() {
this.color = ['red', 'green']
}
function Child() {
// ps: call,apply,bind都会绑定this, call,apply立即执行
Father.call(this);
}
const instance1 = new Child();
instance1.color.push('white') // ['red', 'green', 'white']
const instance2 = new Child();
instance2.color.push('balck') // ['red', 'green', 'balck']
优点:
(1): 可以向父类进行传参
function Father(color) {
this.color = color
}
function Child() {
// ps: call,apply,bind都会绑定this, call,apply立即执行
Father.call(this, 'white');
}
(2): 引用类型在多子类实例中互不影响
缺点:
(1): 必须在构造函数中定义方法,所以函数不能重用
(2): 子类不能访问父类原型上的方法
2.原型链继承
function Father() {
this.property = true;
}
Father.prototype.getSuperValue = function() {
return this.property;
}
function Child() {
this.property = false;
}
Child.prototype = new Father()
const child = new Child()
child.getSuperValue() //false
原型与继承关系:判断原型链上是否有该构造函数
(1): instance instanceof Object
(2): Object.prototype.isPrototypeof(instance)
原型链的问题:
(1): 原型链上的引用类型会在多个实例间共享。
(2): 子类在实例化时不能向父类传参
3.组合继承
因为原型链继承和构造函数继承都有一定的缺点,导致这两种方式都不适合单独使用,所以利用这两种继承的优势来实现继承
通过原型链来继承原型上的属性和方法,通过构造函数继承实例属性。
function Father(name) {
this.name = name;
this.color = ['red', 'blue']
}
Father.prototype.getValue = function() {
return 'getValue'
}
function Child(name1) {
Father.call(this, name1)
}
Child.prototype = new Father()
const child1 = new Child('test-name1')
const child2 = new Child('test-name2')
// 此时实例child1和child2有自己的属性name,color不相互影响,共用原型上的getValue方法
组合继承是使用最多的继承方式,但是有一定的缺点,就是构造韩式执行力两次,一次是在Father.call()时调用,另一次是在原型继承Child.prototype = new Father()时调用
4.原型式继承
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
let person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
let anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
let yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie"
原型式继承的缺点与原型链继承相同
5.寄生继承
function createAnother(original){
let clone = object(original); // 通过调用函数创建一个新对象
clone.sayHi = function() { // 以某种方式增强这个对象
console.log("hi");
};
return clone; // 返回这个对象
}
寄生继承会导致函数难以复用
6.寄生组合继承
因为组合继承有一定的缺点,所以寄生组合继承弥补了相关缺点。寄生组合继承取得父类原型的副本赋给子类原型,这样就不用执行两次构造函数了,也就不会在原型上还有父类上的构造函数属性
function inheritPrototype(subType, superType) {
let prototype = object(superType.prototype); // 创建对象
prototype.constructor = subType; // 增强对象
subType.prototype = prototype; // 赋值对象
}
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
};
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
// 或者使用Object.create()来实现,Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型
// Child.prototype = Object.create(Father.prototype)
SubType.prototype.sayAge = function() {
console.log(this.age);
};
7.Promise
promise的话,我前面已经总结过,就不再次进行赘述了,主要也是参考阮一峰的es6文档(https://es6.ruanyifeng.com/#docs/promise)
// promise解决了层层回调的回调地狱问题,async,await将异步流程以同步的表现形式体现出来。
// Promise.prototype上的方法
Promise.prototype.then
Promise.prototype.catch
Promise.prototype.finally
// Promise上的方法
Promise.all // Promise.all([p1, p2, p3]), 当都成功时,将所有状态都返回,当有状态失败,将失败状态返回
Promise.race //Promise.race([p1, p2, p3]), 将先完成的状态进行返回
Promise.allSettle //Promise.allSettle([p1,p2,p3]), 当所有状态都变换时(不论成功和失败),将所有状态返回
Promise.any //Promise.any([p1, p2, p3]), 当有一个成功或所有都失败时进行返回
8.BOM
window: 表示浏览器实例
location: 当前窗口地址栏的一些信息
navigator: 包含的浏览器信息
screen: 浏览器的屏幕信息
history: 用户的导航历史信息
9.客户端存储
cookie:用于存储一些登录信息,限制有大小限制和个数限制, 不要再cookie中存储敏感信息,会不安全
localStorage:本地存储
sessionStorage: 浏览器存储会话数据,浏览器关闭时内容清空
© 著作权归作者所有
发表评论