JS高级
基础总结
-
基本类型:String Number Boolean undefined null
引用类型:Object 任意对象 Function Array(函数和数组都是一种特别的对象)
-
判断:
-
typeof 返回值是字符串,表示类型的小写字符串。不能判断null和object object和array
-
=:只有undefined和null可以用全等判断 如 undefined=undefined,因为他们都只有一个值。
-
instanceof 判断对象的具体类型,前者是不是后者的实例
-
-
undefined与null的区别:undefined表示定义了未赋值;null表示赋值了,值为null
-
什么时候给变量赋值null:初始赋值null,表明将要把这个变量赋值为对象;最后赋值为null,使指向的对象变成垃圾对象,被浏览器回收
-
严格区别变量类型与数据类型:数据类型分为基本类型和对象类型;变量类型(变量内存值得类型)分为基本类型和引用类型。
-
数据:存储在内存中,代表特殊信息得东西,本质上是0101
特点:可传递,可运算
一切皆数据,内存中所有的操作目标都是数据。可以进行的运算:算数、逻辑、赋值、运行函数
-
内存:内存条通电后产生的可存储数据的空间(临时的),断电后内存空间和数据消失
分类:栈:存全局变量和局部变量,空间小
堆:存对象,空间大
-
变量:可变化的量,由变量名和变量值组成,每个变量都对应一块小内存,变量名就是用来查找对应的内存,变量值就是内存中保存的数据。
三者关系:内存是用来存储数据的空间。硬盘存储空间大,但操作慢。内存小但是读写快。
变量是内存的标识。
JS引擎管理内存:内存生命周期:分配小内存空间,得到他的使用权——存储数据,可以反复进行操作——释放小内存空间
释放内存:局部变量:函数执行完自动释放; 对象:成为垃圾对象,后面由垃圾回收器回收
-
对象:多个数据的封装体(用来保存多个数据的容器),可以统一管理多个数据。
语法:. 和[],当属性名包含特殊字符如- 空格,或属性名不确定(变量)时,用[]读取属性
-
函数:实现特性功能的n条语句的封装体。只有函数是可以执行的,其他类型的数据不能执行。函数可以提高代码复用,便于阅读交流。
调用方法:test()∶直接调用;obj.test():通过对象调用;new test(): new调用;test.call/apply (obj):临时让test成为obj的方法进行调用,这种方式可以调用不属于自己的方法。
-
回调函数:定义了,但是没用调用,但是执行了就是回调函数。常见的dom事件回调函数——this是发生事件的dom元素,定时器回调函数——this是window,ajax请求回调函数,生命周期回调函数
-
立即执行的函数IIFE:就是匿名函数自调用,作用就是实现隐藏,不污染全局命名空间
函数高级
-
函数的prototype属性:每个函数都有个prototype属性,默认指向一个Object空对象(原型对象)。原型对象中有一个constructor,指向函数对象。即fun.prototype.constructor === fun
-
每个函数function都有一个prototype,即显式原型;每个实例对象都有一个
__proto__
,隐式原型。 -
对象的隐式原型的值为其对应构造函数的显式原型的值。
实例对象.__proto__===构造函数.prototype
。这里保存的都是地址值
函数的prototype属性:在定义函数时自动添加的,默认值是一个空0bject对象
对象的
__proto__
属性:创建对象时自动添加的,默认值为构造函数的prototype属性值程序员能直接操作显式原型,但不能直接操作隐式原型(ES6之前)
-
原型链:访问对象的属性或方法时,先从自身属性中查找,如果没有,沿着
__proto__
这条链向上查找,找到返回,如果还没找到,继续向上找,直到找到原型链的尽头,返回undefined。构造函数的实例对象自动拥有构造函数原型对象的属性(方法) -
所有函数的
__proto__
都是一样的,都是Function.prototype -
函数的显示原型指向的对象默认是空Object实例对象(Object例外)
Function.prototype instanceof Object //true Object.prototype instanceof Object //false
-
所有函数都是Function的实例,包括Function本身
Function.prototype.__proto__ === Object.prototype true
-
Object的原型对象是原型链尽头
Object.prototype.__proto__ //null
-
A instanceof B ,如果B函数的显式原型对象在A对象的原型链上,返回true,否则false。也就是判断
A.__proto__(__proto__可以有多次) === B.prototype
-
声明提升var是声明未赋值。用function定义的函数有函数提升,可以提前使用。如果用var xx= function(){}不能提前使用,他是变量提升。变量提升早于函数提升
-
执行上下文:
全局执行上下文
在执行全局代码前将window确定为全局执行上下文。对全局数据进行预处理:
- var定义的全局变量==>undefined,添加为window的属性
- function声明的全局函数==>赋值(fun),添加为window的方法
- this==>赋值(window)
开始执行全局代码
函数执行上下文
在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象对局部数据进行预处理
- 形参变量==>赋值(实参)==>添加为执行上下文的属性
- arguments==>赋值(实参列表),添加为执行上下文的属性
- var定义的局部变量==>undefined,添加为执行上下文的属性
- function声明的函数==>赋值(fun),添加为执行上下文的方法
- this==>赋值(调用函数的对象)
开始执行函数体代码
在全局代码执行前,JS引擎就会创建一个栈来存储管理所有的执行上下文对象
在全局执行上下文(window)确定后,将其添加到栈中(压栈),window永远在栈底
在函数执行上下文创建后,将其添加到栈中(压栈)
在当前函数执行完后,将顶部的对象移出栈(出栈)
当所有代码执行完后,栈中只剩下window
-
作用域与作用域链
作用域可以理解为一个代码段所在的区域,是静态的(相对于上下文对象),在编写代码时就确定了。
ES6之前只有全局作用域和函数作用域,ES6之后有块作用域{}里的语句,外面不可见。用于隔离变量,不同作用域下同名变量不会有冲突。
函数作用域是在函数定义时就已经确定了的,而不是在函数调用时。
作用域和执行上下文的区别
全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时;全局执行上下文环境是在全局作用域确定之后,js代码马上执行之前创建;函数执行上下文是在调用函数时,函数体代码执行之前创建作用域是静态的,只要函数定义好了就一直存在,且不会再变化;执行上下文是动态的,调用函数时创建,函数调用结束时就会自动释放
联系
执行上下文环境(对象)是从属于所在的作用域全局上下文环境==>全局作用域
函数上下文环境==>对应的函数使用域
作用域链就是链式由内向外查找变量的过程。
-
闭包
当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包。
闭包是嵌套的内部函数,有些人理解为包含被引用变量的对象。即一个认为是内部函数,一个认为是外部的整体函数。闭包存在于嵌套的内部函数中,需要有函数嵌套,且内部函数引用了外部函数的数据,(还要执行外部函数,内部只需要执行函数定义就可以产生闭包,不用调用函数)才能产生。
闭包作用
使用函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期);让函数外部可以操作(读写)到函数内部的数据(变量/函数)
函数执行完后,函数内部声明的局部变量一般是不存在的,存在于闭包中的变量才可能存在。
在函数外部不能直接访问函数内部的局部变量。但是可以通过闭包让外部操作他
闭包生命周期
产生:在嵌套内部函数定义执行完时就产生了(不是在调用)。
死亡:在嵌套的内部函数成为垃圾对象时。
应用
定义js模块:定义具有特定功能的js文件,将所有的数据和功能都封装在一个函数内部(私有的),只向外暴露一个包含n个方法的对象或函数,模块的使用者只需要通过模块暴露的对象调用方法来实现对应的功能。
缺点
函数执行完后,函数内的局部变量没有释放,占用内存时间会变长;容易造成内存泄露(意外的全局变量、没有及时清理的计时器或回调函数、闭包)
对象高级
-
创建对象的模式:
- Object构造函数模式:var obj={}
- 对象字面量模式: var obj ={xx:xx, xxx:xxx}
- 构造函数模式:function Person(name,xx){xx:xx, this.xxx=function(){}},这些都是Object的实例
- 构造函数+原型的组合模式(自定义):function Person(){} Person.prototype.方法=function(){}
-
继承模式:
-
原型链继承:得到方法
function Parent(){} Parent.prototype.test = function(){} function child(){} child.prototype = new Parent()//子类型的原型指向父类型实例Child.prototype.constructor = child var child = new child();//有test()
-
借用构造函数:得到属性
function Parent(xxx){this.xxx = xxx} Parent.prototype.test = function(){} function child(xxx,yyy){ Parent.call(this, xxx);//借用构造函数this.Parent( xxx) } var child = new child( 'a', 'b'); //child.xxx为'a ',但child没有test()
-
组合
function Parent(xxx){this.xxX = xxx} Parent.prototype.test = function(){} function Child(xxx, yyy){ Parent.call(this, xxx);//借用构造函数this.Parent( xxx) }
-
-
new一个对象背后做了些什么?
创建一个空对象——给对象设置
_proto_
,值为构造函数对象的prototype属性值——执行构造函数体(给对象添加属性/方法)
线程机制与进程机制
-
线程:是进程内的一个独立执行单元,是程序执行的一个完整流程,是CPU最小的调度单元
-
相关知识
- 应用程序必须运行在某个进程的某个线程上,一个进程中至少有一个运行的线程:主线程,进程启动后自动创建。
- 一个进程中也可以同时运行多个线程,我们会说程序时多线程运行的。
- 一个进程内的数据可以供其中的多个线程直接共享。
- 多个进程之间的数据是不能直接共享的。
- 线程池:保存多个线程对象的容器,实现线程对象的反复利用
-
JS事件的执行机制前面我写过了~这里就不再赘述
-
H5的Worker对象:分线程事件对象。分线程中的全局对象不是window,不可以调用window的方法,也不可以更新界面。不能跨域,慢,且不是每个浏览器都支持这个新特性。