一、数据类型
1、分类
基本(值)类型
● String: 任意字符串
● Number: 任意的数字
● boolean: true/false
● undefined: undefined
● null: null
对象(引用)类型
● Object: 任意对象
● Function: 一种特别的对象(可以执行)
● Array: 一种特别的对象(数值下标,内部数据是有序的)
2、判断数据类型的方式
● typeof:返回的是一个字符串,可以判断: undefined/数值/字符串/布尔值/function,注意,不能判断null与
object,object与array
● instanceof:判断对象的具体类型
● ===:可以判断 undefined, null. 这是因为这两个类型的值就一个
注意:=== 和 == 的区别:
==是比较两个值是否相等,如果不相等会做一次数据类型转换再进行比较
=不会进行数据类型转换, 使用=时, 除了比较值相等以外, 还比较类型是否相等
3、undefined与null的区别
● undefined代表定义但是还未进行赋值
● null表示定义并且赋值了,只是值为null
4、什么时候给变量赋值为null
● 初始赋值,表示将要赋值为对象
● 结束前,让对象成为垃圾对象(被垃圾回收器回收)
5、严格区别变量类型与数据类型
● 数据类型:
○ 基本类型
○ 对象类型
● 变量类型
○ 基本类型:保存的就是基本类型的数据
○ 引用类型:保存的是地址值
6、值传递与引用传递
在JS调用函数时传递变量参数时,是值传递还是引用传递?
理解1:都是值(基本/地址值)传递
理解2:可能是值传递,也可能是引用传递(地址值)
var obj1 = {name: 'Tom'}
var obj2 = obj1
obj2.age = 12
console.log(obj1.age) //12
function fn(obj){
obj.name = 'A'
}
fn(obj1)
console.log(obj2.name) //A
var a = {age:12}
var b = a
a = {name: 'Bob', age: 13}
console.log(b.age, a.name, a.age) //12 Bob 13
function fn2(obj){ //传过来是a对象的地址值,obj相当于新建的一个对象,这个对象指向传过来的a的对象
obj = {age: 15} //obj指向了一个新对象
}
fn2(a)
console.log(a.age) //13
7、js引擎如何管理内存
● 内存生命周期
○ 分配小内存空间,得到它的使用权
○ 存储数据,可以反复进行操作
○ 释放小内存空间
● 释放内存
○ 局部变量:函数执行完后自动释放
○ 全局变量:成为垃圾对象==>垃圾回收器回收
注意下面一段代码:
<script type="text/javascript">
var a = 3
var obj = {}
obj = null //此时,对象只剩下两个,一个a,一个就是指向null的obj对象,上一步obj指向的对象变为垃圾对象,被垃圾回收器回收,但是指向null的obj对象不会释放内存空间
</script>
二、JS的函数
1、IIFE
● 理解
全称:Immediately-Invoked Function Expression(立即调用函数表达式或立即执行函数)
● 作用
○ 隐藏实现
○ 不会污染外部(全局)命名空间
○ 用它来编码js模块
<script type="text/javascript">
(function () {
var a = 3
console.log(a+3)
})()
var a = 4
console.log(a)
</script>
2、函数中的this
● this是什么
○ 任何函数本质上都是通过某个对象来调用的,如果没有直接指定就是window
○ 所有函数内部都有一个变量this,它的值是调用函数的当前对象
3、函数的prototype
● 函数的prototype属性
○ 每个函数都有一个prototype属性,它默认指向一个Object空对象(即称为:原型对象,指没有我们自己定义
的属性)
○ 原型对象中有一个属性constructor,它指向函数对象(构造函数)
● 给原型对象添加属性(一般都是方法)
○ 作用:函数的所有实例对象自动拥有原型中的属性(方法)
4、显示原型与隐式原型
- 每个函数function都有一个prototype,即显式原型
- 每个实例对象都有一个\_\_proto\_\_,可称为隐式原型
- 对象的隐式原型的值为其对应构造函数的显式原型的值
- 内存结构
- 总结:
- 函数的prototype属性:在定义函数时自动添加的,默认值是一个空Object对象
- 对象的__proto__属性:创建对象时自动添加的,默认值为构造函数的prototype属性值
- 程序员能直接操作显式原型,但不能直接操作隐式原型(ES6之前)
- 函数的prototype属性:在定义函数时自动添加的,默认值是一个空Object对象
5、原型链
- 所有函数的显式原型指向的对象默认是空Object实例对象(但Object不满足)
- 所有函数都是Function的实例(包含Function)
- Object的原型对象的原型是原型链的尽头
console.log(Function.__proto__ === Function.prototype) //true
console.log(Object.prototype.__proto__) //null
原型笔记:
6、instanceof
- 表达式:A instanceof B
- 如果B函数的显式原型对象在A对象的原型链上,则返回ture,否则返回false
function Foo(){}
var f1 = new Foo();
console.log(f1 instanceof Foo) //true
console.log(f1 instanceof Object) //true,注意:f1.__proto__ = Foo.prototype,而Foo.prototype.__proto__ == Object.prototype
7、原型链面试题
function A() {
}
A.prototype.n = 1
var b = new A()
A.prototype = {
n: 2,
m: 3
}
var c = new A()
console.log(b.n, b.m, c.n, c.m) //1, undefined, 2, 3
/***************************************************************************************/
var F = function () { }
Object.prototype.a = function () {
console.log('a()')
}
Function.prototype.b = function () {
console.log('b()')
}
var f = new F()
F.a() //a()
F.b() //b()
f.a() //a()
f.b() //报错
三、执行上下文与执行上下文栈
1、变量声明提升
- 通过var定义的变量,在定义语句之前就可以访问到
- 值:undefined
2、函数声明提升
- 通过function声明的函数,在声明之前就可以直接调用
- 值:函数定义(对象)
3、问题:变量提升和函数提升是如何产生的?
在下面内容作出解释
4、代码分类(位置)
- 全局代码
- 函数(局部)代码
5、全局执行上下文
- 在执行全局代码前将window确定为全局执行上下文
- 对全局数据进行预处理
- var定义的全局变量==>undefined,添加为window的属性
- function声明的全局函数==>赋值(fun),添加为window的方法
- this ==> 赋值(window)
- 开始执行全局代码
6、函数执行上下文
- 在调用函数,准备执行函数之前,创建对应的函数执行上下文对象
- 对局部数据进行预处理
- 形参变量==>赋值(实参)==>添加为执行上下文的属性
- arguments==>赋值(实参列表),添加为执行上下文的属性
- var定义的局部变量==>undefined,添加为执行上下文的属性
- function声明的函数==>赋值(fun),添加为执行上下文的方法
- this==>赋值(调用函数对象)
- 开始执行函数体代码
四、闭包
1、如何产生闭包
当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包。执行函数定义就产生了闭
包,不需要调用内部函数
function fn1() {
var a = a
function fn2() {
console.log(a)
}
}
fn1()
2、闭包到底是什么
- 使用chrome调试查看
- 理解1:闭包是嵌套的内部函数(绝大部分人)
- 理解2:包含被引用变量(函数)的对象(极少数人)
- 注意:闭包存在于嵌套的内部函数中
3、产生闭包的条件
- 函数嵌套
- 内部函数引用了外部函数的数据(函数/变量)
4、常见的闭包
-
将函数作为另一个函数的返回值
function fn1(){ var a = 2 function fn2(){ a++ console.log(a) } return fn2 } var f = fn1() f() //3 f() //4 //这种情况只产生一个闭包,注意闭包的产生条件:执行函数就产生了闭包,不需要调用 //怎么看闭包产生了几个?只需看外部函数调用了几次,闭包就产生了几次
-
将函数作为实参传递给另一个函数调用
function showDelay(msg, time){ setTimeout(function(){ alert(msg) }, time) } showDelay('atguigu', 2000)
5、闭包的作用
- 使用函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期)
- 让函数外部可以操作(读写)到函数内部的数据(变量/函数)
问题:
● 函数执行完后,函数内部声明的局部变量是否还存在? 一般不存在,存在于闭包中的变量才可能存在
● 在函数外部能直接访问函数内部的局部变量吗? 不能,但我们可以通过闭包让外部操作它
6、闭包的缺点
● 函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
● 容易造成内存泄漏
解决:
● 能不用闭包就不用
● 及时释放
7、内存溢出与内存泄漏
● 内存溢出
○ 一种程序运行出现的错误
○ 当程序运行需要的内存超过了剩余的内存时,就抛出内存溢出的错误
● 内存泄漏
○ 占用的内存没有及时释放
○ 内存泄漏积累多了就容易导致内存溢出
○ 常见的内存泄漏
■ 意外的全局变量
■ 没有及时清理的计时器或回调函数
■ 闭包
五、面向对象高级
1、继承
● 原型链继承
○ 套路
■ 定义父类型构造函数
■ 给父类型的原型添加方法
■ 定义子类型的构造函数
■ 创建父类型的对象赋值给子类型的原型
■ 将子类型原型的构造属性设置为子类型
■ 给子类型原型添加方法
■ 创建子类型的对象:可以调用父类型的方法
○ 关键
子类型的原型为父类型的一个实例对象
<script type="text/javascript">
//父类型
function Supper() {
this.supProp = "Supper property"
}
Supper.prototype.showSupperProp = function () {
console.log(this.supProp)
}
//子类型
function Sub() {
this.subProp = "Sub property"
}
//子类型的原型为父类型的一个实例对象
Sub.prototype = new Supper()
//让子类型的原型的constructor指向子类型
Sub.prototype.constructor = Sub
Sub.prototype.showSubProp = function () {
console.log(this.subProp)
}
var sub = new Sub()
sub.showSupperProp()
</script>
● 借用构造函数继承(假的)
○ 套路
■ 定义父类型构造函数
■ 定义子类型构造函数
■ 在子类型构造函数中调用父类型构造
○ 关键
在子类型构造函数中调用call()–调用父类型构造函数
<script type="text/javascript">
function Person(name, age) {
this.name = name
this.age = age
}
function Student(name, age, price) {
Person.call(this, name, age)
this.price = price
}
var s = new Student('Tom', 20, 14000)
console.log(s.name, s.age, s.price)
</script>
● 组合继承
使用 原型链+借用构造函数的组合继承
○ 利用原型链实现对父类型对象的方法继承
○ 利用super(),借用父类型构造函数初始化相同属性
<script type="text/javascript">
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.setName = function (name) {
this.name = name
}
function Student(name, age, price) {
Person.call(this, name, age) //为了得到属性
this.price = price
}
Student.prototype = new Person() //为了能看到父类型的方法
Student.prototype.constructor = Student //修正constructor属性
Student.prototype.setPrice = function (price) {
this.price = price
}
var s = new Student('Tom', 24, 15000)
s.setName('Bob')
s.setPrice(16000)
console.log(s.name, s.age, s.price)
</script>
六、线程机制与事件机制
H5 Web Workers(多线程)
1、介绍
Web Workers 是 HTML5 提供的一个javascript多线程解决方案,我们可以将一些大计算量的代码交由web
Worker运行而不冻结用户界面,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变
JavaScript单线程的本质
2、使用
创建在分线程执行的worker.js文件
function fibonacci(n){
return n <= 2? 1 : fibonacci(n-1) + fibonacci(n-2) //计算斐波那系数,递归调用
}
var onmessage =function (event){ //不能用函数声明
var number = event.data
console.log('分线程接受到主线程发送过来的数据: ' + number)
//操作发送过来的数据
var result = fibonacci(number)
postMessage(upper);//将获取到的数据发送给主线程
console.log('分线程向主线程返回数据: ' + result)
}
在主线程中的js中发消息并设置回调
var input = document.getElementById('number')
document.getElementById('btn').onclick = function() {
var number = input.value
//创建一个Worker对象并向它传递将在新线程中执行的脚本的URL
var worker = new Worker("worker.js");
//接收worker传过来的数据函数
worker.onmessage = function (event) {
console.log('主线程接收分线程返回的数据: ' + event.data);
};
//向分线程发送数据
worker.postMessage(number);
}
3、缺点
- 慢
- 不能跨域加载JS
- worker内代码不能访问DOM(更新UI)
- 不是每个浏览器都支持这个新特性