预解释
- 变量和函数的预解释只发生在当前的作用于中
-
js中内存的分类
- 栈内存:用来提供一个js代码指定的环境 —>作用域(全局作用域和局部作用域)
- 堆内存:用来存储引用类型的值 ->对象存储的是键值对,函数储存的是字符串
-
函数执行的时候形成一个私有作用域
- 1.有形参给形参赋值,形参也是函数的局部变量,把实参赋值一份给形参(值类型和引用类型不同的情况)2.进行私有作用域预解释 3.私有作用域从上倒下执行
- 作用链的机制:冒泡向上查找(作用域链模式查找)
- 闭包: 函数形成了一个新新的私有作用域保护了里面的私有作用域不受外界的干扰(外界改变不了私有的,私有的也改变不了外面的)
- 函数不使用var声明的变量,指定函数时是绑定到window上的
- 预解释的时候,不管条件成立不成立都会进行预解释
- 在js中如果变量的名字和函数的名字重复了,也算冲突
- 如何查找当前作用域上一级作用域?看当前的函数是在那个作用域定义的,和执行没有关系
- 内存的释放和作用域销毁
-
堆内存:
- 对象数据类型或者函数数据类型在定义的时候首先开辟一个堆内存,对内存有一个引用地址,如果外面有变量知道了这个地址,我们就说这个内存被占用了,就不能销毁
- 我们想要让堆内存释放/销毁,只需要把所有她的变量设置为null即可,如果当前的堆内存没有任何东西被占用,那么浏览器会在空闲的时候把它销毁...
-
栈内存
- 全局作用域
- 私有作用域(只有函数执行才能产生私有作用域)
- 一般情况下,函数执行会形成一个私有的作用域,当私有作用域执行完成后,都会主动的释放和销毁(即函数执行时开辟内存空间,执行完成后内存空间会被释放和销毁)
-
特殊情况下,当前私有作用域中的部分内存被作用域以为的变量占用了,那么当前的作用域就不能销毁了(地址被其他占用,就不能够销毁)
function fn () { var num = 100; return function () { } } var f = fn() // 私有作用域被外界占用不会销毁 fn()() // 不立即销毁私有作用域,当返回的函数执行执行完毕后,再销毁
-
自执行函数形成的作用域也不会销毁(dom的click绑定函数也会占用作用域以外的内存,私有作用域也不会销毁)
~function () { oDiv.onclick = function () { .... //原理都是一样的,都是函数地址被别的占用不销毁 } }
js中的this
- js中的this主要是函数中函数的this
- js中的this代表当前执行的主体,js的contenxt代表的是当前的执行环境(this和执行上下文没必然的联系)
- this是谁和函数在哪定义和在哪执行没有必然的联系
this在函数中5种情况
-
函数执行,首先看函数名之前,有没有.,有点.的话之前是谁,this就是谁
function fn() { console.log(this) } var obj = {fn:fn}; obj.fn() // this 指向fn function sum (){ fn() } sum() // this指向window var o = { sum: function () { fn() } } o.sum() // this指向window
-
- 自执行函数中的this永远指向window
-
- 给元素的某一个事件绑定一个方法,当事件触发的时候,执行对应的方法,方法中的this是当前的元素
document.getElementById('#app').onclick = function(){ console.log(this) // this ->app元素对象 fn() // this指向window }
- 在构造函数当中,类中(函数体中)出现的this指向当前new创建的实例
设计模式
单利模式
- 对象数据类型:把同一个事物的属性和方法放在同一个孔家下,起到了分组的作用,不同事物之间的属性即使属性名相同,也不会发生冲突,这种分组编写代码的模式叫做单利模式(模块化开发的原理)
- 缺点: 单利模式不能够批量生产
工厂模式
- 把实现同一件是事情的相同代码放在一个函数中,以后如果想实现这个功能,不需要重新在编写这个代码,直接调用函数即可 --> 函数的封装
-
缺点:不能够识别调用者的身份
function createPerson (name, age) { var obj = {}; obj.name = name; obj.age = age; obj.call = function() { .... } return obj }
-
构造函数模式
-
构造函数目的:就是为了创建一个自定义类
function createPerson (name, age) { var obj = {}; obj.name = name; obj.age = age; obj.call = function() { .... } return obj } function CreatePerson (name, age) { <!-- this指向当前的实例(注意和普通函数的区别) --> this.name = name; this.age = age; this.call = function() { .... } } var p1 = new createPerson('章三', 18)
-
工厂函数和构造函数的区别
-
执行的时候
- 普通函数执行 --> createPerson()
- 构造函数执行 ——> new createPerson() // 通过new创建出来一个实例(p1)
-
-
在函数执行的时候
- 相同点: 都是形成一个私有的作用域,然后形参赋值 ->预解释 ->代码自上而下执行
-
不同点:构造函数在代码执行之前,不用再手动创建对象(new的作用)
- 1.浏览器会默认创建一个对象(而这个对象就是我们new创建出来的实例),
- 2.然后把属性值和属性名赋值给当前的实例,
- 3.最后浏览器会把创建的这个实例返回
-
检测数据类型的方式:
- typeof(): 检测基本的数据类型
- instanceof():检测某一个实例是否属于这个类
- attr in Obj: 检测某一个属性(共有和私有)是否属于这个对象
- hasOwnProperty: 用来检测一个私有属性是够属于这个对象
原型模式
- 基于构造函数的圆形模式解决了方法和属性共有的问题
- 1.每一个函数类型(函数,类)都有一个天生自带的属性:prototype,并且这个属性是一个对象数据类型的值
- 并且在prototype上天生给他加了一个属性constructor(构造函数),属性值是当前函数本身
-
3.每一个对象数据类型(普通独享,实例,prototype...)也天生自带一个属性:__proto__,属性值是当前实例所属类或函数的原型(prototype)
function Fn() { this.x = 100 } Fn.prototype.add = function () { console.log(this.x) } var f1 = new Fn(); var f2 = new Fn(); f1.hasOwnproperty('x')
- Object是所有对象类型的基类
- 在Object.prototype上是没有__proto__属性(是唯一没有的)
-
再实例对象f1上没有hasOwnProPerty这个属性?
- 通过对象名.属性名 的形式获取属性值的时候,首先在对象的私有属性进行查找,若果私有中存在这个属性,则获取这个私有属性值;若果没有,通过__proto__找到所属类的原型(具有类原型定义的公共方法和属性),原型存在的,获取共有的属性值;如果原型上也没有,则继续通过原型的__proto__继续查找,直到找到Object.prototype为止,这种查询模式叫做"原型链模式"
- 原型链模式遵行冒泡形式就近原型
- 所有类都是函数数据类型的,所有类的原型都是对象数据类型的
- Fuction函数类,所有的函数数据类型都是它的一个实例
-
再内置类原型上扩展我们的方法
Array.prototype.mgUnique = function(){ var obj = {} for(var i=0;i<this.length;i++){ var cur = this[i] if(obj[cur]=== cur){ this[i] = this[this.length-1]; this.length--; i-- continue; } obj[cur] = cur;//目的是为了链式写法 } obj = null; return this } (es6)
-
批量添加共有方法
function Fn(){ this.x = 100 } Fn.prototype = { // 重构原型的指向 constructor: Fn, // 手动添加constructor a: function(){ ... }, b:function(){ ... } }
-
克隆一个对象的方式
-
原生实现
function cloneObj(obj){ var obj2 = {}; for(var key in obj) { if(obj.hasOwnproperty(key)){ obj2[key] = obj[key] } } retuen obj2 }
-
Object.create(proObj): 创建一个新的对象,把proObj当作新创建对象的原型,IE8下不兼容
function object(o){ function Fn(){ } Fn.prototype = o; return new Fn; }
-
-
1.原型继承
-
B.prototype = new A;
-
原型链继承的特点:子类B继承了父类A所有的属性和方法
#div.__proto__ -> HTMLDivElement.prototype -> HTMLElement.prototype ->Element.prototype -> Node.prototype ->EventTarget.prototype -> Object.prototype(Dom原型继承的原理) function Object() { ... } Object.prototype = { constructor:Object, hasOwnProperty: function(){ ... } }; function EventTarget () { ... } <!-- EventTarget创建对象接可以使用Object的所有方法--> EventTarget.prototype = new Object(); EventTarget.prototype.addEventListenter = function(){ ... } function Node(){ ... } Node.prototype = new EventTarget(); Node.prototype.createElement = function(){ ... }
- 原型继承并不是把父类中的属性和方法克隆一份给子类,而是让子类和父类增加了原型链的链接,哟吼子类获取父类的方法,需要一级一级的向上查找来使用
-
2. call继承
-
把父类的私有方法和属性克隆一份,作为子类的私有属性
function A(){ this.x = 100; } A.prototype.getX = function(){ console.log(this.x) } function B(){ <!-- this -> n --> A.call(this) // A.call(n) 把修改this的指向,并让A执行 } var n = new B()
3.冒充对象继承
4.混合模式继承
-
原型继承+call继承
function A(){ this.x = 100; } A.prototype.getX = function(){ console.log(this.x) } function B(){ <!-- this -> n --> A.call(this) // A.call(n) 把修改this的指向,并让A执行 } B.prototype = new A; B.prototype.constaructor = B var n = new B()
5.寄生混合式继承
function A(){
this.x = 100;
}
A.prototype.getX = function(){
console.log(this.x)
}
function B(){
<!-- this -> n -->
A.call(this) // A.call(n) 把修改this的指向,并让A执行
}
B.prototype = Object.create(A.prototype);
B.prototype.constaructor = B
var n = new B()
函数的三种角色
function Fn(){
var num = 500;
this.x = 100;
}
Fn.prototype.getX = function(){
console.log(this.x)
}
Fn.aaa = 1000;
var f = new Fn();
f.num // undefined
f.aaa // undefined
var res = Fn() // this指向undefined
Fn.aaa // 1000
-
函数在整个js中是最复杂也是最重要的知识
-
一个函数存在了多面性
- 普通函数:本身就是一个函数,执行的时候形成私有的左右域(闭包),形参赋值,预解释,代码执行,执行完成后内存销毁/不销毁
- 类:它有自己的实例,也有一个叫prototype属性是自己的原型,它的实例都可以通过__proto__指向自己的原型
- 普通对象:和 var obj = {}中的obj一样,就是一个普通对象,他作为对象可以有一些自己的私有属性,也可以通过__proto__找到Function.prototype对象
-
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。