2月3
Day8
1-什么是构造函数?
通过 new 函数名 来实例化对象的函数叫构造函数。任何的函数都可以作为构造函数存在。
之所以有构造函数与普通函数之分,主要从功能上进行区别的,构造函数的主要 功能为 初始
化对象,特点是和new 一起使用。new就是在创建对象,从无到有,构造函数就是在为初始化
的对象添加属性和方法。构造函数定义时首字母大写
var arr = new Array()
2-创建对象的方式有哪些?举例说明
调用系统的构造函数创建对象
var obj = new Object(); //构造函数
添加属性
obj.name = "小白";
添加方法
obj.play = function() {
console.log("打游戏");
}
获取对象的属性或者是调用方法
console.log(obj.name);
obj.play();
工厂函数和new Object创建对象 创建多个对象
function createObj(name) {
var obj = new Object();
obj.name = name;
obj.xw = function() {
console.log(this.name + "不好");
}
return obj
}
实例化
var aa = createObj("小白");
aa.xw();
var bb = createObj("小红");
console.log(aa, bb);
构造函数创建对象
定义构造函数
function c(name) {
this.name = name;
this.sayHi = function() {
console.log("你好");
}
}
使用构造函数创建对象
var sun = new c("吕布");
console.log(sun);
字面量创建对象
var obj = {
name: "华佗",
sayHi: function() {
console.log("医术高超")
}
}
设置和获取属性的另一种方法
obj[name] = "华佗";
console.log(obj["name"]);
obj["sayHi"]();
3-js 种实现继承的方式
1.原型链继承
原型链继承通过修改子类的原型为父类的实例,从而实现子类可以访问到父类构造函数以及原型上的属性或者方法。
让我们来实现一下:
function Parent() {
this.name = 'fedaily'
}
Parent.prototype.getName = function() {
return this.name;
}
function Child() {}
// 这里也可以直接写出Child.prototype = Parent.prototype
// 但是这样就不能访问到父类的构造函数的属性了,即this.name
Child.prototype = new Parent()
var child = new Child()
child.getName() // fedaily
优点
实现逻辑简单
缺点
父类构造函数中的引用类型(比如对象/数组),会被所有子类实例共享。其中一个子类实例进行修改,会导致所有其他子类实例的这个值都会改变
2.构造函数继承
构造函数继承其实就是通过修改父类构造函数this实现的继承。我们在子类构造函数中执行父类构造函数,同时修改父类构造函数的this为子类的this。
我们直接看如何实现:
function Parent() {
this.name = ['fedaily']
}
function Child() {
Parent.call(this)
}
var child = new Child()
child.name.push('fe')
var child2 = new Child() // child2.name === ['fedaily']
优点
解决了原型链继承中构造函数引用类型共享的问题,同时可以向构造函数传参(通过call传参)
缺点
所有方法都定义在构造函数中,每次都需要重新创建(对比原型链继承的方式,方法直接写在原型上,子类创建时不需要重新创建方法)
3.组合继承
同时结合原型链继承、构造函数继承就是组合继承了。
function Parent() {
this.name = 'fedaily'
}
Parent.prototype.getName = function() {
return this.name
}
function Child() {
Parent.call(this)
this.topic = 'fe'
}
Child.prototype = new Parent()
// 需要重新设置子类的constructor,Child.prototype = new Parent()相当于子类的原型对象完全被覆盖了
Child.prototype.constructor = Child
优点
同时解决了构造函数引用类型的问题,同时避免了方法会被创建多次的问题
缺点
父类构造函数被调用了两次。同时子类实例以及子类原型对象上都会存在name属性。虽然根据原型链机制,并不会访问到原型对象上的同名属性,但总归是不美。
4.寄生组合继承
寄生组合继承其实就是在组合继承的基础上,解决了父类构造函数调用两次的问题。我们来看下如何解决的:
function Parent() {
this.name = 'fedaily'
}
Parent.prototype.getName = function() {
return this.name
}
function Child() {
Parent.call(this)
this.topic = 'fe'
}
// 仔细看这个函数的实现
inherit(Child, Parent)
function inherit(child, parent) {
var prototype = object(parent.prototype)
prototype.constructor = child
child.prototype = prototype
}
// 这个函数的作用可以理解为复制了一份父类的原型对象
// 如果直接将子类的原型对象赋值为父类原型对象
// 那么修改子类原型对象其实就相当于修改了父类的原型对象
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
优点
这种方式就解决了组合继承中的构造函数调用两次,构造函数引用类型共享,以及原型对象上存在多余属性的问题。是推荐的最合理实现方式(排除ES6的class extends继承哈哈哈)。
缺点
没有啥特别的缺点
5.ES6继承
ES6提供了class语法糖,同时提供了extends用于实现类的继承。这也是项目开发中推荐使用的方式。
使用class继承很简单,也很直观:
class Parent {
constructor() {
this.name = 'fedaily'
}
getName() {
return this.name
}
}
class Child extends Parent {
constructor() {
// 这里很重要,如果在this.topic = 'fe'后面调用,会导致this为undefined,具体原因可以详细了解ES6的class相关内容,这里不展开说明
super()
this.topic = 'fe'
}
}
const child = new Child()
child.getName() // fedaily
4-什么是闭包?有什么作用?
概念:函数A中有一个函数B,函数B 总是可以访问函数A中的数据或者变量(不严谨),这样就形成了闭包
1.一种就是上面提到的使函数外部能够读取到函数内部的变量;
2.让函数内部的变量一直隐藏/保存在内存中(JavaScript的垃圾回收机制:解释器检测到没有程序在使用这个对象了,当它确定其是无用的时候,JavaScript的解释器就把会把该对象所占用的内存释放掉)。
5-什么是预解析?
预解析指的就是,在js文件或者script里面的代码在正式开始执行之前,进行的一些解析工作。这个工作很简单,就是在全局中寻找var关键字声明的变量和通过function关键字声明的函数

浙公网安备 33010602011771号