构造函数、创建对象、继承、闭包、预解析
(1)Son.prototype = Father.prototype
弊端:Son.prototype.constructor 指向Father,需要手动更改为Son ;Son的实例化对象只能继承Father原型中的方法,无法继承Father本身的属性。
(2)Son.prototype = new Father()
弊端:Son.prototype.constructor 指向Father,需要手动更改为Son;Son的实例化对象共享Father自身的引用类型属性。什么意思呢?下面举个栗子:
function Father(){ this.name = "zs" ; this.arr = [1,2,3] }
function Son(){ }
Son.prototype = new Father()
var s1 = new Son(), s2 = new Son();
s1.arr.push(5);
console.log(s1.arr)--------> [1,2,3,5]
console.log(s2.arr)--------->[1,2,3,5]
看了这里例子就明白了,Son的实例化对象s1,s2继承了Father的属性arr,但是s1,s2是同时指向这一属性的。
2,借助构造函数继承
function Father(){ this.name = "zs"; this.age=38 };
function Son(){ Father.call( this) / Father.apply(this) }
弊端:Son只能继承Father自身的属性,而无法继承Father原型中的方法。
3,组合式继承
将原型链继承与构造函数结合起来
function Father(){ this.name="zs";this.age=38 }
Father.prototype.sayHi = function(){ alert("hello") }
function Son(){ Father.call(this) }
Son.prototype = new Father()
var s = new Son() ;
弊端:通过Father.call() 和 new Father() ,父类构造函数Father被调用了两次。
4,原型式继承
function createObj(o){ function F(){ } F.prototype=o ; return new F() }
var obj = { name:"zs" , age:18, sayHi:function(){ } }
var newObj = createObj( obj );
newObj继承了obj的属性和方法,但是同样出现了共享父类中引用类型属性的问题。
5,经典继承(Es5中的新语法:存在兼容性问题,需要做浏览器的能力检测)
function create(obj){
if(Object.create){ return Object.create(obj) }
else { function F(){ } F.prototype=o ; return new F() }
}
6,寄生式继承(类似于原型式继承)
function createObj(o){ function F(){ } F.prototype=o ; return new F() }
function createObj2(o){ var obj = createObj(o) ; obj.sayHi = function(){ } return obj }
var obj = { name:"zs" , age:18, sayHi:function(){ } }
var newObj = createObj2(obj)
newObj继承了obj的属性和方法,但是同样出现了共享父类中引用类型属性的问题。
7,寄生组合式继承(组合继承+寄生继承)
function createObj(o){ function F(){ } F.prototype=o ; return new F() }
function inheritPrototype(Child, Father) {
var prototype = object(Father.prototype);//创建对象
prototype.constructor = Child;//增强对象
Child.prototype = prototype;//指定对象 }
function Father(name) {
this.name = name;
this.arr = [1, 2, 3, 4]; }
Father.prototype.sayName = function () { console.log("父类原型" + this.name); }
function Child(name, age) { Father.call(this, name); this.age = age; }
inheritPrototype(Child, Father)
Child.prototype.sayAge = function () { console.log(this.age); }
var child1 = new Child() , child2 = new Child();
child1.arr.push(5) ------> [1,2,3,4,5]
child2.arr ------> [1,2,3,4].
优点:可以多重继承 解决两次调用 解决实例共享引用类型的问题 原型链保持不变
如果一个函数用到了它作用域外面的变量,那么这个变量和这个函数之间的环境就叫闭包。

闭包的用途有些什么:
1.模仿块级作用域
所谓块级作用域就是指在循环中定义的变量,一旦循环结束,变量也随之销毁,它的作用范围只在这一小块。而在JavaScript中没有这样的块级作用域,由于JavaScript不会告诉你变量是否已经被声明,所以容易造成命名冲突,如果在全局环境定义的变量,就会污染全局环境,因此可以利用闭包的特性来模仿块级作用域。

在上面的代码中,闭包就是那个匿名函数,这个闭包可以当函数X内部的活动变量,又能保证自己内部的变量在自执行后直接销毁。这种写法经常用在全局环境中,可以避免添加太多全局变量和全局函数,特别是多人合作开发的时候,可以减少因此产生的命名冲突等,避免污染全局环境。
2.储存变量
闭包的另一个特点是可以保存外部函数的变量,内部函数保留了对外部函数的活动变量的引用,所以变量不会被释放。

这种写法可以用在把一些不经常变动计算起来又比较复杂的值保存起来,节省每次的访问时间。
3.封装私有变量
我们可以把函数当作一个范围,函数内部的变量就是私有变量,在外部无法引用,但是我们可以通过闭包的特点来访问私有变量。

(1)<变量预解析—变量提升>
变量提升,分为全局作用域变量提升和局部变量提升
<1>全局变量提升,在预解析阶段,浏览器的JS引擎,会将所有 变量 的声明部分,都提到所在作用域(整个页面代码)的 最前端

<2>局部变量提升:在函数内部 ,局部变量在预解析时,会把 变量 的声明部分 ,提升到当前函数内部的最前端, 然后再执行

(2)<函数预解析—函数提升>
在浏览器预解析阶段,浏览器的JS引擎会将所有 声明式函数 都提到所在作用域的 最前端, 之后再进行代码由上而下执行
只有声明式函数才有函数提升,匿名方式和对象方式创建的函数,在预解析时不会做函数提升


浙公网安备 33010602011771号