一、本地存储 (1次)
-
Cookies:
-
容量4k
-
会为请求自动存储cookies数据,方便但浪费流量
-
每个站点仅能存储20个
-
API较原始,需要自动封装
-
-
localStorage
-
容量5M
-
永久存储,除非手动删除
-
兼容IE8+,操作方便
-
-
sessionStorage
-
功能与localStorage相似,但关闭页面后自动清除
-
属于会话级别的,不能在同源窗口进行共享
-
-
Web SQL
-
使用SQL语句进行操作
-
已废弃
-
-
IndexedDB
-
容量>=250M,甚至没有上限
-
一种NoSQL数据库,用键值对进行存储,可以进行快速读取操作
-
二、js数据类型 (2次)
首先,js数据类型分为两大类型:基本类型和引用类型
基本类型包括:Undefined,Null,Boolean,Number,String,symbol,bigint
引用类型包括:
1、原生对象:Array,Object,Date,function
2、内置对象:(JS提供的不依赖执行宿主的对象)Math、Global
3、宿主对象:(JS提供的任何依赖宿主的对象)window
基本类型和引用类型的区别:
1、基本类型使用栈进行存储,引用类型使用堆进行存储
2、基本数据类型占据空间小且固定,引用数据类型占据空间大不固定
如何判断数据类型:
typeof:
对于对象、数组、日期的判断均会返回object
- typeof(null) => “object”
- typeof(数组/正则/日期/对象) => “object”
instanceof:
console.log(2 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log('str' instanceof String); // false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object); // true
利用instancecof可以判断当前实例是否属于该类,但不能用于处理基本数据类型
只要出现在实例的原型链上的类,检测结果都是TRUE(页面中可以手动更改原型链的指向,这样导致检测结果不一定准确)
constructor:
console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true
console.log(({}).constructor === Object); // true
相对于instanceof来说,constructor可以判断基本数据类型
检测结果不一定准确,( 和instanceof一样,均可以在其原型链上修改原型指向 )
Object.prototype.toString( ): (最优检测方法)
- 其它类的原型上的toString基本上都是转换为字符串的,只有Object原型上的是检测数据类型的返回结果 “[object 所属的类]”
- Object.prototype.toString执行,其中的this是谁,就是检测谁的数据类型
- Object.prototype.toString.call(xxx)
- ({}).toString.call(xxx)
如何判断数组:
方法一:使用Object.prototype.tostring()
function isArray(arg) {
return Object.prototype.toString.call(arg) === '[object Array]'
}
let arr = [1,2,3]
isArray(arr) // true
方法二:使用ES6新增语法Array.isArray(arr)
let arr = [1,2,3]
Array.isArray(arr) // true
三、继承
原型链继承:
通过改造原型链的方式,利用原型链的语法实现继承
1、优点:
- 实现简单,只需要设置子类的prototype为父类的实例即可;
- 可以通过子类中的实例直接访问父类原型链中的属性和函数
2、缺点:
- 子类的所有实例将共享父类,若属性为引用值,子类的实例改变的时候会修改这个属性值,就会影响其他实例的值;
- 在创建子类实例的时候,无法向父类的构造函数中传递参数;
- 注意给子类的原型对象上添加属性或者方法时,一定要写在 children.prototype=new Father 后
构造函数继承
在子类中用apply()、call()的方法,调用父类构造函数实现继承。
1、优点:
- 在子类的构造中通过call来改变了父类中的this指向,导致父类构造函数中定义的属性或方法都赋值给了子类,生成的每一个子类实例都具有这些属性和方法,且互不影响;
- 创建子类的时候,可以向父类的构造函数中传递参数。
2、缺点:
- 只能继承父类中实例的属性和方法,而不能继承父类原型上的属性和方法;
- 父类的构造函数中添加一个实例方法,其对应的子类也拥有该实例方法,每新创建一个子类,就小有一个父类实例方法,会导致占用内存比较大。
拷贝继承
先创建父类的实例,通过for...in循环来遍历父类实例中的所有属性和方法,并依次赋值给子类,同时原型上的属性和函数也赋给子类的实例上。
1、优点:
- 可以实现向父类中的构造方法中传递参数;
- 能够实现让子类继承父类中的实例属性,实例方法以及原型对象上的属性和方法。
2、缺点:
父类的所有属性和方法,子类都需要复制拷贝一边,比较消耗内存
组合继承:
组合继承的核心思想是将构造函数继承和原型链继承两种方式组合起来,共享的属性、方法用原型链继承实现,独享的属性、方法用借用构造函数实现
1、优点:
- 可以向父类构造函数中传递参数
- 通过
Father.call(this,xxx);
这个行代码,可以将父类中的实例属性和方法添加到子类中,另外通过Children.prototype = new Father();
可以将父类的原型对象上的属性和函数绑定到子类
的原型对象上。
2、缺点:
父类实例属性会绑定两次,一次在子类中call()调用实现绑定,另一次是在改写子类prototype时实现绑定操作,但是通过call()
函数完成父类中实例属性和方法的绑定的优先级要高于通过改写子类prototype
的方式,即call的调用会覆盖掉prototype的绑定内容。
寄生式组合继承:
寄生式组合继承和组合继承的区别是:寄生式定义了一个新的空对象,并利用子类的原型指向父类副本的实例从而实现原型共享,解决了组合继承多次绑定的问题。
class实现extends继承:
// 继承关键字 => extends
class Person {
constructor (name, age) {
this.name = name
this.age = age
}
jump () {
console.log('会跳')
}
}
class Teacher extends Person {
constructor (name, age, lesson) {
super(name, age) // extends 中, 必须调用 super(), 会触发执行父类的构造函数
this.lesson = lesson
console.log('构造函数执行了')
}
sayHello () {
console.log('会打招呼')
}
}
let teacher1 = new Teacher('zs', 18, '体育')
console.log(teacher1)
四、(常考)什么是原型链(2-3):
在javascript中,绝大多数的构造函数,除了一些内建函数以外,都有一个叫做prototype的属性指向他的原型,每当创建一个新的子类实例的时候,这个子类都可以共享这个原型中的属性,例如我们经常使用的hasownproperty和tostring都是在object这个原型对象上的属性。
每一个对象都有一个__proto__属性指向其构造函数的原型,这个对象就可以利用这个__proto__属性将自己和原型对象连接起来,而同时这个原型对象也有自己的__proto__属性,因此形成了一个链状结构,这就是原型链。当我们去访问一个对象的属性的时候,如果访问不到就会沿着原型链一层一层的寻找下去,最终到达Object.prototype。