JavaScript进阶
数据类型
-
基本类型
-
String:任意字符串
-
Number:任意的数字
-
Boolean:true/false
-
undefined:undefined
-
null:null
-
-
对象类型
-
Object:任意对象
-
Function:一种特别的对象(可以执行)
-
Array:一种特别的对象(数值下标,内部数据是有序的)
-
-
判断
-
typeof
可以判断:undefined / 数值 / 字符串 / 布尔值 / function
不能判断:null与object object与array
-
instanceof
判断对象的具体类型
-
===
-
相关问题
-
undefined和null的区别?
-
undefined代表定义未赋值
-
null定义并赋值了,只是值为null
-
-
什么时候给变量赋值为null?
-
初始赋值,表明将要赋值为对象
-
结束前,让对象成为垃圾对象(被垃圾回收器回收)
-
-
严格区别变量类型与数据类型?
-
数据的类型
-
基本类型
-
对象类型
-
-
变量的类型(变量内存值的类型)
-
基本类型:保存的是基本类型的数据
-
引用类型:保存的是地址值
-
-
数据 变量 内存
-
什么是数据?
-
存储在内存中代表特定信息的东西
-
数据的特点:可传递,可运算
-
一切皆数据
-
内存中所有操作的目标:数据
-
算术运算
-
逻辑运算
-
赋值
-
运行函数
-
-
-
什么是内存?
-
内存条通电后产生的可储存数据的空间(临时的)
-
内存的产生和死亡:内存条(电路板)—通电—产生内存空间—存储数据—处理数据—断电—内存空间和数据都消失
-
一块小内存的2个数据
-
内存分类
-
栈:全局变量/局部变量,函数名
-
堆:对象
-
-
-
什么是变量?
-
可变化的量,由变量名和变量值组成
-
每个变量都对应的一块小内存,变量名用来查找对应的内存,变量值就是内存中保存的数据
-
-
内存,数据,变量三者之间的关系
-
内存用来存储数据的空间
-
变量是内存的标识
-
数据 变量 内存 的相关问题
-
var a=xxx,a内存中保存的是什么?
-
xxx是基本数据,保存的就是这个数据
-
xxx是对象,保存的是对象的地址值
-
xxx是一个变量,保存的xxx的内存内容(可能是基本数据,也可能是地址值)
注意:
-
n个引用变量指向同一个对象,通过同一个变量修改对象内部数据,另一个变量看到的是修改之后的数据。
-
2个引用变量指向同一个对象,让其中一个引用变量指向另一个对象,另一引用变量依然指向前一个对象。
-
函数调用执行后,它里面的局部变量会被释放掉。
2.在JS调用函数时传递变量参数,是值传递还是引用传递?
-
理解1:都是值(基本值/地址值)传递
-
理解2:可能是值传递,也可能是引用传递(地址值)
3.JS引擎如何管理内存?
-
内存生命周期
-
分配小内存空间,得到它的使用权
-
存储数据,可以反复进行操作
-
释放小内存空间
-
-
释放内存
-
局部变量:函数执行完自动释放
-
对象:成为垃圾对象—垃圾回收器回收
-
对象
-
什么是对象?
-
多个对象的封装体
-
用来保存多个数据的容器
-
一个对象代表现实中的一个食物
-
-
为什么要用对象?
-
统一管理多个数据
-
-
对象的组成?
-
属性:属性名(字符串)和属性值(任意)组成
-
方法:一种特殊的属性(属性值是函数)
-
-
如何访问对象内部数据?
-
对象.属性名:编码简单,但属性名包含特殊字符:- 空格或者属性名不确定的时候不能用
-
对象['属性名']:编码复杂,但能通用
-
函数
-
什么是函数?
-
实现特定功能的n条语句的封装体
-
只有函数是可以执行的,其他类型的数据不能执行
-
-
为什么要用函数?
-
提高代码复用
-
便于阅读交流
-
-
如何定义函数?
-
函数声明
-
表达式
-
-
如何调用(执行)函数?
-
test():直接调用
-
obj.test():通过对象调用
-
new test():new调用
-
test.call/apply(obj):临时让test成为obj的方法进行调用
-
回调函数
-
什么函数才是回调函数?
-
我定义的
-
我没有调用
-
但最终它执行了
-
-
常见的回调函数?
-
dom事件回调函数
-
定时器回调函数
-
ajax请求回调函数
-
生命周期回调函数
-
IIFE
-
理解
-
全称
-
-
作用
-
隐藏实现
-
不会污染外部(全局)命名空间
-
最终用它来编写JS模块
-
函数中的this
-
this是什么?
-
任何函数本质上都是通过某个对象来调用,如果没有直接指定就是window
-
所有函数内部都有一个变量this,它的值是调用函数的当前对象
-
-
如何确定this中的值?
-
test():window
-
p.test():p
-
new test():新创建的对象
-
p.call(obj):obj
-
语句分号问题
-
在下面2种情况下不加分号会有问题
-
小括号开头的前一条语句
-
中方括号开头的前一条语句
-
解决办法:在行首加分号
-
原型prototype
-
函数的原型prototype属性
-
每个函数都有一个prototype属性,它默认指向一个Object空对象(即称为:原型对象)
-
原型对象中有一个属性constructor,它指向函数对象
-
-
给原型对象添加属性(一般是方法)
-
作用:函数的所有实例对象自动拥有原型中的属性(方法)
-
显示原型和隐式原型
-
每个函数function都有一个prototype,即显示原型(属性),默认值是一个空Object对象
-
每个实例对象都有一个_ proto _,可称为隐式原型(属性)
-
对象的隐式原型的值为其对应构造函数的显示原型的值
-
内存结构
-
总结
-
函数的prototype属性:在定义函数的时候自动添加的,默认值是一个空Object对象
-
对象的_proto _属性:创建对象时自动添加的,默认值为构造函数的prototype属性值
-
程序员直接操作显示原型,但不能直接操作隐式原型(ES6之前)
-
原型链
-
原型链
-
访问一个对象属性(方法)时,先在自身属性中查找,找到返回;如果没有,在沿着_ proto _这条链向上查找,找到返回;如果最终没找到,返回undefined
-
别名:隐式原型链
-
作用:查找对象的属性(方法)
-
-
构造函数、原型、实体对象的关系
-
函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)
-
所有函数都是Function的实例,包括它本身
-
Object的原型对象是原型链尽头
原型链属性问题
-
读取对象的属性值时:会自动到原型链中查找
-
设置对象的属性值时:不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值
-
方法一般定义在原型中,属性一般通过构造函数定义在对象本身上
探索instanceof
-
instanceof是如何判断的?
-
表达式:A instanceof B
-
如果B函数的显示原型对象在A对象的原型链上,返回true,否则返回false
-
-
Function是通过new自己产生的实例
变量提升与函数提升
-
变量声明提升
-
通过var定义(声明)的变量,在定义语句之前就可以访问到
-
值:undefined
-
-
函数声明提升
-
通过function声明的函数,在之前就可以直接调用
-
值:函数定义(对象)
-
-
问题:变量提升和函数提升是如何产生的?
执行上下文
-
代码分类(位置)
-
全局代码
-
函数(局部)代码
-
-
全局执行上下文
-
在执行代码前将window确定为全局执行上下文
-
对全局数据进行预处理
-
var定义的全局变量—undefined,添加为window的属性
-
function声明的全局函数—赋值(fun),添加为window的方法
-
this—赋值(window)
-
开始执行全局代码
-
-
-
函数执行上下文
-
在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟的,存在于栈中)
-
对局部数据进行预处理
-
形参变量—赋值(实参)—添加为执行上下文的属性
-
arguments—赋值(实参列表),添加为执行上下文的属性
-
var定义的局部变量—undefined,添加为执行上下文的属性
-
function声明的函数—赋值(fun),添加为执行上下文的方法
-
this—赋值(调用函数的对象)
-
-
开始执行函数体代码
-
执行上下文栈
-
在全局代码执行前,JS引擎就会创建一个栈来存储管理所有的执行上下文对象(栈:后进先出)
-
在全局执行上下文(window)确定后,将其添加到栈中(压栈)
-
在函数执行上下文对象后,将其添加到栈中(压栈)
-
在当前函数执行完后,将栈顶的对象移除(出栈)
-
当所有代码执行完后,栈中只剩下window
作用域
-
作用域
-
一块“地盘”,一个代码段所在的区域
-
它是静态的(相对于上下文对象),在编写代码时就确定了
-
-
分类
-
全局作用域
-
函数作用域
-
没有块作用域(ES6有了let)
-
-
作用
-
隔离变量,不同作用域下同名变量不会有冲突
-
作用域与执行上下文
-
区别1
-
全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了,而不是在函数调用时
-
全局执行上下文环境是在全局作用域确定后,js代码马上执行之前创建
-
函数执行上下文是在调用函数时,函数体代码执行之前创建
-
-
区别2
-
作用域是静态的,只要函数定义好了就一直存在,且不会再变化
-
上下文环境是动态的,调用函数时创建,函数调用结束时上下文环境就会被释放
-
-
联系
-
执行上下文(对象)是从属于所在的作用域
-
全局上下文环境—全局作用域
-
函数上下文环境—对应的函数使用域
-
作用域链
-
理解
-
多个上下级关系的作用域形成的链,它的方向是从下向上的(从内到外)
-
查找变量时就是沿着作用域链来查找的
-
-
查找一个变量的查找规则
-
在当前作用域下的执行上下文中查找对应的属性,如果有直接返回,否则进入2
-
在上一级作用域的执行上下文中查找相应的属性,如果有直接返回,否则进入3
-
再次执行2的相同操作,直到全局作用域,如果还找不到就抛出找不到的异常
-
闭包
-
如何产生闭包?
-
当一个嵌套的内部函数引用了嵌套的外部函数的变量(函数)时,就产生了闭包
-
-
闭包是什么?
-
理解一:闭包是嵌套的内部函数
-
理解二:包含被引用变量(函数)的对象
-
注意:闭包存在于嵌套的内部函数中
-
-
产生闭包的条件?
-
函数嵌套
-
内部函数引用了外部函数的数据(变量/函数)
-
执行外部函数
-
常见的闭包
-
将函数作为另一个函数的返回值
-
将函数作为实参传递给另一个函数调用
闭包的作用
-
使用函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期)
-
让函数外部可以操作(读写)到函数内部的数据(变量/函数)
问题:
-
函数执行完后,函数内部声明的局部变量是否还存在?
-
一般不存在,存在于闭包中的不是垃圾对象的变量才可能存在
-
-
在函数外部能直接访问函数内部的局部变量吗? 闭包
-
不能,但通过闭包操作它
-
闭包的生命周期
-
产生:在嵌套内部函数定义执行完就产生了
-
死亡:在嵌套的内部函数成为垃圾对象时
?函数定义执行和函数执行
闭包的应用 定义JS模块
-
具有特定功能的js文件
-
将所有的数据和功能都封装在一个函数内部(私有的)
-
只想外暴露一个包括n个方法的对象或函数
-
模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能
闭包的缺点及解决
-
缺点
-
函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
-
容易造成内存泄露
-
-
解决
-
及时释放
-
内存溢出与内存泄露
-
内存溢出
-
一种程序运行出现的错误
-
当程序运行需要的内存超过了剩余的内存时,就抛出内存溢出的错误
-
-
内存泄露
-
占用的内存没有及时释放
-
内存泄露积累多了容易导致内存溢出
-
常见的内存泄露
-
意外的全局变量
-
没有及时清理的计时器或回调函数
-
闭包
-
-
对象创建模式
-
Object构造函数模式
-
套路:先创建空Object对象,再动态添加属性和方法
-
适用场景:起始时不确定对象内部数据
-
问题:语句太多
-
-
对象字面量模式
-
套路:使用{}创建对象,同时指定属性和方法
-
适用场景:起始时对象内部数据是确定的
-
问题:如果创建多个对象,有重复代码
-
-
工厂模式
-
工厂函数:函数return返回的是一个对象的函数
-
套路:通过工厂函数动态创建对象并返回
-
适用场景:需要创建多个对象
-
问题:对象没有一个具体模型,都是Object类型
-
-
自定义构造函数模式
-
套路:自定义构造函数,通过new创建对象
-
适用场景:需要创建多个类型确定的对象
-
问题:每个对象都有相同的数据,浪费内存
-
-
构造函数+原型的组合模式
-
套路:自定义构造函数,属性在函数中初始化,方法添加到原型上
-
适用场景:需要创建多个类型确定的对象
-
原型链继承
-
原型链继承
-
定义父类型构造函数
-
给父类型的原型添加方法
-
定义子类型的构造函数
-
创建父类型的对象赋值给子类型的原型
-
将子类型原型的构造属性设置为子类型
-
给子类型原型添加方法
-
创建子类型的对象:可以调用父类型的方法
-
关键:子类型的原型为父类型的一个实例对象
-
进程与线程
-
进程
-
程序的一次执行,它占有一片独有的内存空间
-
可以通过Windows任务管理器查看进程
-
-
线程
-
是线程的一个执行单元
-
-
相关知识
-
应用程序必须运行在某个进程的某个线程上
-
一个进程中至少有一个运行的线程:主线程,进程启动后自动创建
-
一个进程中也可以同时运行多个线程,我们会说程序是多线程运行的
-
一个进程内的数据可以供其中的多个线程直接共享
-
多个线程之间的数据是不能直接共享的
-
线程池(thread pool):保存多个线程对象的容器,实现线程对象的反复利用
-
-
何为多进程与多线程?
-
多进程运行:一个应用程序可以同时启动多个实例运行
-
多线程:在一个进程内,同时有多个线程运行
-
-
比较单线程与多线程?
-
多线程:能有效提升CPU的利用率;创建多线程开销,线程间切换开销,死锁与状态同步问题
-
单线程:顺序编程简单易懂;效率低
-
-
JS是单线程还是多线程?
-
JS是单线程运行的
-
但使用H5中的Web Workers可以多线程运行
-
-
浏览器运行是单线程还是多线程?
-
多线程
-
-
浏览器运行是单进程还是多进程?
-
单进程:Firefox ,老版IE
-
多进程:chrome,新版IE
-
浏览器内核
-
内核由很多模块组成
-
js引擎模块:负责js程序的编译与运行
-
html,css文档解析模块:负责页面文本的解析
-
DOM/CSS模块:负责dom/css在内存中的相关处理
-
布局和渲染模块:负责页面的布局和效果的绘制(内存中的对象)
-
定时器模块:负责定时器的管理
-
事件响应模块:负责事件的管理
-
网络请求模块:负责Ajax请求
-
定时器引发的思考
-
定时器是定时执行吗?
-
定时器并不能保证定时执行
-
-
定时器回调函数是在分线程执行的吗?
-
在主线程中执行,js是单线程的
-
-
定时器如何实现的?
-
事件循环模型
-
js是单线程执行的
-
如何证明js执行是单线程的?
-
setTimeout()的回调函数是在主线程执行的
-
定时器回调函数只有在运行栈中的代码全部执行完后才有可能执行
-
-
为什么js要用单线程模式,而不用多线程模式?
-
JavaScript的单线程,与它的用途有关
-
作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM,这决定了它只能是单线程,否则会带来复杂的异步问题
-
-
代码的分类
-
初始化代码
-
回调代码
-
-
js引擎执行代码的基本流程
-
先执行初始化代码:包含一些特别的代码 回调函数(异步执行)
-
设置定时器
-
绑定事件监听
-
发送Ajax请求
-
在后面某个时刻才会执行回调代码
-
事件循环类型
H5 Web Workers多线程
-
优点:可以让js在分线程执行
-