- 2.
- 3.
- 4.内存
- 5.引用变量
- 6.
- 8.函数
- 9.常见回调
- 10.IIFE(Immediately-Invoked Function Expression)
- 11.this
- 14.复习
- 15-18.原型链
- 20.instanceof判断逻辑
- 22-24.变量提升、上下文及上下文栈
- 27.作用域
- 30.闭包
- 31.常见的闭包
- 33.闭包的生命周期
- 34.缺点
- 35.内存溢出与泄露
- 37.创建对象的方法
- 38-39.继承
- 42.进程和线程
- 46.事件循环机制
- 47.web worker:实际上使用不多
- 21.原型链面试题
- 1.
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 - 2.
function F(){} //易错点: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() //无输出
F.a() //a()
F.b() //b() - 1.
var a =3
function fn(){
console.log(a)
var a = 4
}
fn() //undefined - 1.
console.log(‘gb’+i)
var i =1
foo(1)
function foo(i){
if(i == 4){
return
}
console.log(‘fb’+i)
foo(i+1)
console.log(‘fe’+i)
}
console.log(‘ge’+i)
/
共执行5个上下文
输出分别为:gbundefined fb1 fb2 fb3 fe3 fe2 fe1 ge1
/ - 2.
function a(){}
var a
console.log(typeof a) //’function’ - 3.
if(!(b in window)){
var b = 1
}
console.log(b) //undefined - 4.
var c =1
function c(c){
console.log(c)
}
c() //报错:声明变量/声明函数/变量赋值 - 1.
var x = 10
function fn(){
console.log(x)
}
function show(f){
var x = 20
f()
}
show(fn) //10,作用域关乎结构,不关乎逻辑 - 2.
var fn = function(){
console.log(fn)
}
fn() //f(){console.log(fn)} - 2.
var name2 = ‘The Window’
var object = {
name2:’My Object’,
getNameFunc: function(){
var that = this
return function(){
return that.name2
}
}
}
alert(object.getNameFunc()()) //’My Object’
/
##1 直接调用匿名函数,具有全局性。
/ - 3.
function fun(n,o){
console.log(o)
return{
fun:function(m){
return fun(m,n)
}
}
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3) //undefined; 0; 0; 0
fun(0).fun(1).fun(2).fun(3) //undefined 0 1 2
var c = fun(0).fun(1); c.fun(2); c.fun(3) //undefined 0; 1; 1
2.
1.三等号和两等号区别
两等号会做数据类型转换,三等号不会。e.g.
1 == '1' //true
1 === '1' //false
拓展:
(1)隐式类型转换:
1)+的字符串连接符:存在字符串则为字符串连接符,全部转为String
+的算数运算符:不存在字符串则为算数运算符,全部转为Number
2)数值运算及关系运算符:a.全部转为Number类型进行比较;b.两部都是字符串则比较unicode;c.undefined和null两等号相等;d.NaN和任何都不等
3)复杂数据(数组、对象)比较:会先调用valueOf方法
4)逻辑非运算:将数据转化为Boolean类型,0、-0、NaN、undefined、null、’’、false、document.all()会得到false,其余均为true
总结:除字符串连接符和逻辑非运算,都转为Number类型比较;
(2)布尔值的逻辑运算:true && false === false true || fasle === true
非布尔值的逻辑运算:返回原值,哪里停下返回哪个值。或/且运算返回原值而非布尔值,但在if判断中会隐式转换为布尔值。
(3)字符串的比较:Unicode编码比较
数据
存储在内存中表示特定信息的东西,本质上是0101。
3.
1.类型、实例
2.undefined与null的区别:
3.赋值null的时机:
(1)初始赋值null,表示该变量将要赋值为对象;
(2)断开连接,释放内存
4.严格区分变量类型和数据类型
数据类型:基本类型、对象类型
变量类型(变量内存值的类型):
基本类型:保存的是基本类型的数据
引用类型:保存的是地址值
4.内存
内存内容有两种数据:基本数据、地址值数据。
所有内存都有地址值,但只有内存是对象才用到地址值。
一切皆数据,内存是用来存储数据的空间,变量是内存的标识。
5.引用变量
引用变量:变量指向内存的内存内容是地址值,即变量指向对象。
引用变量赋值时,把变量内容赋值给另外的变量,只是内容是地址值。
函数调用时,实参赋值是生成新变量(形参),并把实参的值(基本数据或地址值数据)复制给新变量(形参)。形参在函数内部进行运算,不影响实参(除非是对象.属性,此时形参实参指向同一个对象)
6.
问题:JS调用函数传递变量,是值传递还是引用传递?
理解1:都是值(基本/地址值)传递
理解2:可能是值传递,也可能是引用传递(地址值)
释放内存的两种方法:
1.局部变量-执行函数时生成,执行完毕自动释放
2.对象-成为垃圾对象后被垃圾回收器回收
8.函数
函数:能够实现特定功能的代码封装体
声明方式:表达式声明的同时赋予函数名,那么这个函数名就相当于函数的一个局部变量。只能在内部调用,外部调用则报错。
调用方式:直接调用、对象调用、new调用、call/apply调用
call/apply方法可以让函数临时成为任意指定对象的方法进行调用,此时函数的this指向指定的对象。
9.常见回调
dom事件回调、定时器回调、ajax请求回调、生命周期回调
10.IIFE(Immediately-Invoked Function Expression)
作用:隐藏实现、避免污染全局命名空间、用于编码JS模块
11.this
任何函数本质上都是通过对象调用的,函数内部变量this指向调用该函数的对象,没有指明则是window。
14.复习
1.数据
存储在内存中代表特定信息的东西,本质是0101二进制
具有可读和可传递的基本特性
万物皆数据,函数也是数据
程序中所有操作的目标:数据。算数运算、逻辑运算、赋值、调用函数传参等。
2.内存:内存条通电后产生的存储空间
(1)分配内存:声明变量和函数或创建对象时,JS引擎会自动为此分配一定大小的内存来存放对应的数据
(2)释放内存:清空内存中的数据,标识内存可以再分配使用
自动释放:栈空间的局部变量
垃圾回收期回收:堆空间的垃圾对象
(3)内存中包含2个数据:
内部存储的数据(一般数据/地址数据)
内存地址值数据
(4)内存分类:
栈:全局变量,局部变量(空间较小)
堆:对象(空间较大)
3.变量:值可以变化的量,由变量名与变量值组成
一个变量对应一块小内存,变量名用来查找到内存(标识作用),变量值就是内存中保存的内容
4.内存、数据、变量三者之间的关系
内存是用来存储数据的空间
变量是内存的标识,通过变量找到对应内存,进而操作。
15-18.原型链
1.函数的显式原型指向默认空Object实例对象(仅Object不满足):
Object.prototype instanceof Object //false
Function.prototype.proto === Object.prototype //true
new Function().prototype.proto //尽头
=> Function的显式/隐式原型是对象,是Object的实例
instanceof Object //除了尽头,其余都为true
2.所有函数(包括Object)都是Function的实例(包括Function和Object)
Function.proto === Function.prototype //true => Function是Function的实例
Object instanceof Function //true => Object是Function的实例
instanceof Function //除了Function显式原型、尽头、Object实例,其余都为true
3.Object是Function的实例,Function.prototype是Object实例
4.Object的显式原型是原型链的尽头
Object.prototype.proto //null
5.对象显式原型(实例隐式原型)的constructor属性指向对象本身
new Object().proto.constructor === Object //true
let f = new Function()
f.proto.constructor === Function //true
f.prototype.constructor === f //true
6.所有的函数都有显式原型和隐式原型
隐式原型:函数都是由new Function()得来的,是Function是实例
显式原型:函数可以做构造函数
7.Object实例没有显式原型,因为Object实例不是构造函数
new Object().prototype //undefined
Function.prototype.prototype //undefined
20.instanceof判断逻辑
A instanceof B:B的显式原型在A的隐式原型链上则返回true
22-24.变量提升、上下文及上下文栈
1.栈的运作方式
(1)全局代码执行前,JS引擎创建一个栈来存储管理所有的执行上下文
(2)执行全局代码时,先创建全局执行上下文(window)并压栈、进行全局数据预处理:
var定义的全局变量===>undefined,添加为window的属性
function声明的全局函数===>赋值(fun),添加为window的方法
this===>赋值(window)
(3)调用函数时,先创建函数执行上下文,并压栈、进行局部数据预处理:
形参===>赋值(实参)===>添加为执行上下文的属性
arguments===>赋值(实参列表),添加为执行上下文的属性
var定义的局部变量===>undefined,添加为执行上下文的属性
function声明的函数===>赋值(fun),添加为执行上下文的方法
this===>赋值(调用函数的对象)
(4)在当前函数执行完后,将栈顶的对象移除(出栈)
(5)当所有的代码执行完后,栈中只剩下window
队列:先进先出;栈:后进先出
2.变量提升、函数提升:在变量定义前就可以访问(调用)该变量(函数)
在全局执行代码前,已经完成全局上下文对象的创建、压栈及全局数据预处理。此时栈中全局上下文对象中保存着全局变量:undefined、全局方法:地址值,所以可以访问变量和方法。
代码执行时,定义变量的代码更新栈中的数据,定义函数的代码不再执行。
函数提升优先级比变量提升要高(所以函数后执行),且不会被变量声明覆盖,但是会被变量赋值覆盖。
/
面试题:同名变量提升且赋值、函数提升均存在:变量提升=>函数提升=>变量赋值(修改变量)
/
27.作用域
1.理解:一个代码段所在的区域
相对于上下文对象,作用域是静态的,在编写代码时确定了,与代码结构有关,与逻辑无关。
2.作用:隔离变量,不同作用域下同名变量不冲突。
3.作用域和上下文对象区别与联系
(1)区别1
全局作用域在最开始时创建,函数作用域在函数定义(声明函数)时创建;
全局执行上下文是全局作用域确定之后再创建的;而函数执行上下文是调用函数时创建的。
(2)区别2
作用域是静态的,函数定义时创建后一直存在,且不会再发生变化;
执行上下文是动态的,调用函数时创建,函数调用结束自动释放。
(3)联系
上下文环境(对象)是从属于所在的作用域===> 寻找变量:当前作用域对应的上下文对象
全局上下文环境==>全局作用域
函数上下文环境==>对应的函数使用域
4.作用域链与原型链:作用域链找变量,原型链找对象属性。变量是不带点的,对象属性是点后的名称。
若全局定义域没有,直接找a是沿着作用域链找,找不到则报错;window.a是沿着原型链找,找不到则返回undefined。
30.闭包
1.定义:当函数内嵌套一个函数(含方法的对象),且内部函数(对象的方法)引用外部函数的数据时,就形成了闭包。闭包是指被嵌套的内部函数/对象的方法,或者是(内部函数里的)包含被引用变量的对象。
2.产生闭包的条件
(1)函数嵌套,且内部函数引用了外部函数的数据(变量/函数);
(2)调用外部函数,执行内部函数定义,堆内存中产生内部函数对象,(内部函数不需要调用,重点是在堆内存中产生内部函数对象,而不是内部函数入栈)就形成闭包。且每次外部函数调用都形成一个闭包。
3.作用
(1)正常情况下,函数调用结束,函数上下文出栈,局部变量销毁。通过闭包可以延长局部变量的生命周期;
(2)通过闭包,函数外部可以操作(读写)到函数内部的数据(变量/函数)。
31.常见的闭包
1.内部函数(含方法的对象)作为外部函数的返回值
当调用外部函数时,执行内部函数定义语句,堆内存产生内部函数对象,产生闭包。通过全局上下文的全局变量指向该闭包(堆内存的内部函数对象),延长其生命周期。
没有闭包的过程:(1)创建全局上下文,堆内存开辟外部函数对象;(2)执行外部函数,外部函数上下文压栈,堆内存开辟内部函数对象;(3)外部函数执行完毕,外部函数上下文出栈,堆内存内部函数对象回收。
有闭包的过程:(1)(2)不变;(3)全局上下文新建变量保存内部函数对象的地址值;(4)外部函数函数执行完毕,外部函数上下文出栈,局部变量(内部函数名)销毁,但内部函数对象不回收。
——-但是外部函数名和外部函数对象是一直存在的,因为外部函数名一直在全局上下文中。
2.函数作为实参传递给内部函数调用
3.封装JS组件
优点:1.能够在函数外部读取函数内部的数据;2.不会被回收
33.闭包的生命周期
产生:内部函数定义执行完就产生了
死亡:内部函数成为垃圾对象
34.缺点
函数执行完后,函数内的局部变量没有释放,占用内存时间会变长,容易造成内存泄漏。
2.解决方法
能不用闭包就不用
及时释放
35.内存溢出与泄露
1.内存溢出
当程序运行需要的内存超过了剩余的内存时,抛出的一种程序运行错误
2.内存泄漏
占用的内存没有及时释放,内存泄漏积累过多容易导致内存溢出
常见的内存泄漏:意外的全局变量、没有及时清理的计时器或回调函数、闭包
37.创建对象的方法
1.Object构造函数模式
适用于不确定的内部数据,但语句太多。
2.对象字面量模式
适用于确定的内部数据,但批量复杂。
3.工厂模式
调用工厂函数(函数返回值为对象的函数)创建对象,适用于创建多个对象,但没有具体类型。
4.自定义构造函数模式
自定义构造函数,通过new创建对象,适用于创建多个类型确定的对象,但方法在实例上浪费内存。
5.构造函数+原型组合模式
自定义构造函数,属性在函数中初始化,方法添加到原型上。
38-39.继承
1.借用构造函数继承:继承父类的属性
function Student(name,age,price){
Person.call(this,name,age) //相当于this.Person(name,age),实际上 原型链找不到Parent
this.price = price
}
2.原型链继承:继承父类的方法
Sub.prototype = new Supper() //子类的显式原型指向父类的实例,之后实例也是
Sub.prototype.constructor = Sub //修正constructor函数
/
原型链继承:子类型的原型指向夫类型的实例!
继承前子类实例的原型链:子类实例=>子类原型=>尽头
继承后子类实例的原型链:子类实例=>子类原型(父类实例)=>父类原型=>尽头
子类的方法需要继承完再添加到原型上。
/
3.组合继承:原型链继承+借用构造函数继承
4.寄生组合继承:sub.prototype = Object.create(sub.prototype)
在原型链继承中,创建父类实例时,如果传入参数则会导致子类原型(创建的父类实例)多属性混乱。可以采用寄生组合继承避免。
42.进程和线程
进程:程序的一次执行占用一篇独有的内存空间,跨域通过windows任务管理器查看进程。
线程:是进程内的一个独立执行单元,程序执行的一个完整流程,CPU的最小的调度单元。
关系:一个今晨个至少一个线程,程序是在某个进程中的某个线程执行的。
主线程:
JS引擎模块:负责JS程序的编译与运行
html,css文档解析模块:负责页面文本的解析
DOM/CSS模块:复杂dom/css在内存中的相关处理
布局和渲染模块:负责页面的布局和效果的绘制(内存中的对象)
分线程:
定时器模块:负责定时器的管理
事件响应模块:负责事件的管理
网络请求模块:负责Ajax请求
46.事件循环机制
同步和异步任务分别进入不同的执行环境,同步的进入主线程,即主执行栈,异步的进入任务队列。主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。上述过程的不断重复就是Event Loop (事件循环、事件轮询)。
执行过程:1.执行初始化代码,将事件回调函数交给对应模块管理(Web API)。2.当事件发生时,管理模块会将回调函数及其数据添加到回调列队(Callback Queue)中。3.只有当初始化代码执行完后,才会遍历读取毁掉队列的回调函数执行
在事件循环中,每进行一次循环操作称为tick。在进行下一个宏任务前先执行微任务,清空微任务队列。
宏任务主要包含:script(整体代码)、setTimeout、setInterval、I/O、事件监听、setImmediate(Node.js 环境)
微任务主要包含:Promise、MutaionObserver、process.nextTick(Node.js 环境)
setTimeout/Promise 等API便是任务源,而进入任务队列的是由他们指定的具体执行任务。
47.web worker:实际上使用不多
1.API
Worker:构造函数,加载分线程执行的js文件
Worker.prototype.onmessage:用于接收另一个线程的回调函数
Worker.prototype.postMessage:向另一个线程发送消息
2.不足
分线程不能操作DOM(分线程的全局上下文不是window)
不能跨域加载JS
不是每个浏览器都支持这个新特性
3.主分线程之间沟通:
发送消息:实例.postMessage
接收消息:回调形参event : event.data
21.原型链面试题
1.
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
2.
function F(){} //易错点: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() //无输出
F.a() //a()
F.b() //b()
22.上下文
1.
var a =3
function fn(){
console.log(a)
var a = 4
}
fn() //undefined
25.上下文栈面试题
1.
console.log(‘gb’+i)
var i =1
foo(1)
function foo(i){
if(i == 4){
return
}
console.log(‘fb’+i)
foo(i+1)
console.log(‘fe’+i)
}
console.log(‘ge’+i)
/
共执行5个上下文
输出分别为:gbundefined fb1 fb2 fb3 fe3 fe2 fe1 ge1
/
2.
function a(){}
var a
console.log(typeof a) //’function’
3.
if(!(b in window)){
var b = 1
}
console.log(b) //undefined
4.
var c =1
function c(c){
console.log(c)
}
c() //报错:声明变量/声明函数/变量赋值
28.作用域链面试题
1.
var x = 10
function fn(){
console.log(x)
}
function show(f){
var x = 20
f()
}
show(fn) //10,作用域关乎结构,不关乎逻辑
2.
var fn = function(){
console.log(fn)
}
fn() //f(){console.log(fn)}
var obj = {
fn2:function(){
console.log(fn2)
}
}
obj.fn2() //报错
31.闭包
function fn1(){
var a = 2
function fn2(){
a++
console.log(a)
}
return fn2
}
var f = fn1() //执行fn1的时候,堆内存中创建fn2的函数对象,其中保存了fn1的变量a,且f指向fn2函数对象
f() //3
f() //4
36.闭包终极面试题
var name = 'The Window'
var object = {
name:'My Object',
getNameFunc: function(){
return function(){
return this.name
}
}
}
alert(object.getNameFunc()()) //"The window"
//原理我确实不知道,但感觉上是第二次执行时,getNameFunc这个函数已经出栈了,执行环境是window所以this指向window。(等我康康书本咋说