文章目录
原型
显示原型和隐式原型
每个函数function都有一个protopyte,即显式原型(属性)
每个实例对象都有一个_proto_,可称为隐式原型(属性)
对象的隐式原型的值为其构造函数的显示原型的值
总结
函数的prototype属性:在定义函数时自动添加的,默认值是一个空object对象(但是object也是一个函数,且不满足默认值要求)
对象的_proto_属性:创建对象时自动添加的,默认值为构造函数的prototype属性值
在ES6之前,能直接操作显示原型,但不能直接操作隐式原型
function Fn(){ //内部语句:this.prototype = {}
}
console.log(Fn.prototype)
var fn = new Fn() //this._proto_ = Fn.prototype
console.log(fn._proto_)
console.log(fn._proto_=== Fn.prototype)//true
//给原型添加方法
Fn.prototype.test=function(){
console.log('test')
}
fn.test()
原型链
访问同一个属性时,先在自身属性中查找,找到返回;如果没有,再沿着_proto_这条链向上查找,找到返回;如果最终没找到,返回undefined
别名:隐式原型链
作用:查找对象的属性或者方法
如图所示
构造函数/原型/实例对象的关系
o1和o2是两个实例对象
所有函数的_proto_属性都是一样的
function Fn( ){ }
var Fn=new Function() //Function也是构造函数
Function=new Function()
注意
1.函数的显示原型指向的对象默认值是空Object实例对象(但Object不满足)
在以上代码的基础上
console.log(Fn.prototype instanceof Object) //true
console.log(Object.prototype instanceof Object) //false
console.log(Function.prototype instanceof Object) //true
只有Object是例外
2. 所有函数都是Function的实例(包含Function)
console.log(Function._proto_===Function.prototype)//true
3.Object的原型对象是原型链尽头
console.log(Object.prototype._proto_) //null
原型链属性问题
1.读取对象的属性值时,会自动到原型链中查找
2.设置对象的属性值时,不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值
3.方法一般定义在原型中,属性一般通过构造函数定义在对象本身上。
instanceof
作用:判断左边的对象是不是右边的实例
表达式:A instanceof B
如果B函数的显示原型对象在A对象的原型链上,返回true,否则返回false
Function是通过new自己产生的实例
执行上下文
1.代码分类:(根据位置)全局代码,函数(局部)代码
2.全局执行上下文
在执行全局代码前将window确定为全局执行上下文
对全局数据进行预处理(1.var定义的全局变量==>undefind,添加为window属性;2.function声明的全局函数==>赋值(fun),添加为window的方法;3.this==>赋值为全局方法)
开始执行全局代码
3.函数执行上下文
在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟的,存在于栈中)
对局部数据进行预处理
(1.形参变量==>赋值(实参)>添加为执行上下文的属性;
2.arguments>赋值(实参列表),添加为执行上下文的属性)
执行上下文栈
作用域与作用域链
作用域
就是一个“地盘”,一个代码所在的区域。它是静态的(相对于上下文对象),在编写代码时就确定了。
分类
全局作用域
函数作用域
没块作用域(ES6有了)
作用
隔离变量:不同作用域下同名变量不会有冲突
作用域与执行上下文的区别
作用域链
闭包
常见的闭包
1.将函数作为另外一个函数的返回值
function fn1{
var a=1;
function fn2{
a++;
console,log(a);
}
return fn2
}
var f=fn1()
f()//2
f()//3
2.将函数作为实参传递给另一个函数调用
function shoeDelay(msg,time){
seTimeout(function(){
alert(msg)
},time)
}
showDelay('atguigu',2000)
闭包的作用
1.使用函数内部的变量在函数执行完以后,仍然存活在内存中(延长了局部变量的生命周期)
2.让函数外部可以操作(读写)到函数内部的数据(变量/函数)
注意
在函数外不能直接访问函数内部的局部变量
函数执行完之后,函数内部声明的局部变量一般不存在,存在于闭包中的变量才可能存在
闭包的生命周期
产生:在嵌套内部函数定义执行完时就产生了(不是在调用)
死亡:在嵌套的内部函数成为垃圾对象时
闭包的应用
定义JS模块
如果要暴露多个函数,将要暴露的对象封装起来
return {
字符串1:方法1,
字符串2:方法2
}
闭包的缺点和解决
缺点
函数执行完后,函数内的局部变量没有释放,占用内存时间会变长;
容易造成内存泄露
解决
能不用闭包就不用
及时释放
function fn1(){
var arr=new Arry[10000]
function fn2(){
console.log(arr.length)
}
return fn2
}
var f=fn1()
f()
f=null//让函数内部成为垃圾对象--->回收闭包
内存溢出与内存泄漏
内存溢出
一种程序运行出现的错误
当程序运行需要的内存超过了剩余的内存时,就会抛出内容溢出的错误
内存泄漏
占用的内存没有及时释放
内存泄漏积累多了就容易导致内存溢出
常见的内存泄漏:
1.意外的全局变量
2.没有及时清理的计时器或回调函数
3.闭包
对象创建模式
Object构造函数模式
先创建空Object对象,再动态添加属性和方法
适用场景:起始时不确定对象内部数据
问题:语句太多
对象字面量模式
使用{}创建对象,同时指定属性/方法
适用场景:起始时对象内部数据是确定的
问题:如果创建多个对象,有重复代码
工厂模式
通过工厂函数动态创建对象并返回
适用场景:需要创建多个对象
问题:对象没有一个具体的类型,都是Object类型
工厂函数:返回一个对象的函数都是工厂函数
function createPerson(name,age){
var obj={
name:name,
age:age,
setName:function(name){
this.name=name;
}
}
return obj
}
自定义构造函数模式
自定义构造函数,通过new创建对象
适用场景:需要创建多个类型确定的对象
问题:每个对象都有相同的数据,浪费内存
构造函数+原型的组合模式
自定义构造函数,属性在函数中初始化,方法添加到原型上
适用场景:需要创建多个类型确定的对象