对象高级
1 面向对象
1.1 对象创建方式
-
方式一: Object构造函数模式
-
套路: 先创建空Object对象, 再动态添加属性/方法
-
适用场景: 起始时不确定对象内部数据
-
问题: 语句太多
-
/* 一个人: name:"Tom", age: 12 */ var p = new Object() p = {} p.name = 'Tom' p.age = 12 p.setName = function (name) { this.name = name } p.setaAge = function (age) { this.age = age } console.log(p) -
-
方式二: 对象字面量模式
-
套路: 使用{}创建对象, 同时指定属性/方法
-
适用场景: 起始时对象内部数据是确定的
-
问题: 如果创建多个对象, 有重复代码
var p = { name: 'Tom', age: 23, setName: function (name) { this.name = name } } console.log(p.name, p.age) p.setName('JACK') console.log(p.name, p.age) var p2 = { name: 'BOB', age: 24, setName: function (name) { this.name = name } } -
-
方式三: 工厂模式
-
套路: 通过工厂函数动态创建对象并返回
-
适用场景: 需要创建多个对象
-
问题: 对象没有一个具体的类型, 都是Object类型
// 工厂函数: 返回一个需要的数据的函数 function createPerson(name, age) { var p = { name: name, age: age, setName: function (name) { this.name = name } } return p } var p1 = createPerson('Tom', 12) var p2 = createPerson('JAck', 13) console.log(p1) console.log(p2) -
-
方式四: 自定义构造函数模式
- 套路: 自定义构造函数, 通过new创建对象
- 适用场景: 需要创建多个类型确定的对象
- 问题: 每个对象都有相同的数据, 浪费内存
function Person(name, age) { this.name = name this.age = age this.setName = function (name) { this.name = name } } var p1 = new Person('Tom', 12) var p2 = new Person('Tom2', 13) console.log(p1, p1 instanceof Person)
-
方式五: 构造函数+原型的组合模式
- 套路: 自定义构造函数, 属性在函数中初始化, 方法添加到原型上
- 适用场景: 需要创建多个类型确定的对象
function Person (name, age) { this.name = name this.age = age } Person.prototype.setName = function (name) { this.name = name } var p1 = new Person('Tom', 12) var p2 = new Person('JAck', 23) p1.setName('TOM3') console.log(p1) Person.prototype.setAge = function (age) { this.age = age } p1.setAge(23) console.log(p1.age) Person.prototype = {} p1.setAge(34) console.log(p1) var p3 = new Person('BOB', 12) p3.setAge(12)
1.2 公有、私有、静态属性和方法
-
公有属性和公有方法:设置给实例化对象的属性和方法被称作公有属性和公有方法
function A(name) { //公有属性 this.name = name; } A.prototype.do = function () { //公有方法 console.log(1); }
-
私有属性和私有方法:声明在构造函数中的变量或函数,被称作为私有属性和私有方法
function A(name) { //私有属性 var num = 0; //公有属性 this.name = name; //私有方法 function fn() { } } -
静态属性和方法:js中无需实例化就可以调用的方法叫做静态方法。
function A(name) { //私有属性 var num = 0; //公有属性 this.name = name; //私有方法 function fn() { } } //静态属性 A.index = 1; //静态方法 A.say = function () { console.log("hello world") } -
特权方法
function A(name) { //私有属性 var num = 0; //公有属性 this.name = name; //特权方法 this.getName = function () { return this.name; } }
2 new操作符
new操作符做了什么
- 创建一个空对象
obj({}) - 将
obj的[__proto__]属性指向构造函数的原型(即obj.[[__proto__]] = constrc.prototype) - 将构造函数内部的
this绑定到新建的对象obj,执行构造函数(也就是跟调用普通函数一样,只是此时函数的this为新创建的对象obj而已) - 若构造函数没有返回非原始值(即不是引用类型的值),则返回该新建的对象
obj(默认会添加return this)。否则,返回引用类型的值。
//构造函数A
function A(name) {
this.name = name;
// 如果构造函数的返回值是值类型,那么就丢弃掉,依然返回构造函数创建的实例。
// 如果构造函数的返回值是引用类型,那么就返回这个引用类型,丢弃构造函数创建的实例。
return {
"sex": "18"
};
}
//构造函数的原型对象
A.prototype.do = function () {
console.log(1);
}
//手写new
function writeNew(constr) {
//创建一个对象
var o = {};
//把构造函数的this指向当前的对象o 并且向调用构造函数并传入参数 返回值就是A函数的返回值
var result = constr.apply(o, Array.prototype.slice.call(arguments, 1));
//把自定义对象的隐式原型指向构造函数的显式原型
o.__proto__ = constr.prototype;
//判断构造函数的返回值是基础值 还是对象
var isObj = typeof result === 'object' && typeof result != null;
var isFun = typeof result === 'function';
//根据构造函数的返回值 设置new的返回值
return (isObj || isFun) ? result : o;
}
var a1 = new A("lily");
var a2 = writeNew(A, "laowang");
console.log(a1)
console.log(a2)
console.log(a1 instanceof A)
console.log(a2 instanceof A)
console.log(a1.do)
console.log(a2.do)
console.log(a2.do === a1.do)
3 封装、继承、多态
3.1 封装
封装的目的是将信息隐藏,一般来说封装包括封装数据、封装实现,接下来就逐一来看:
3.1.1 封装数据
- 由于JS的变量定义没有private、protected、public等关键字来提供权限访问,因此只能依赖作用域来实现封装特性
3.1.2 封装实现
-
封装实现即隐藏实现细节、设计细节,封装使得对象内部的变化对其他对象而言是不可见的,对象对它自己的行为负责,其他对象或者用户都不关心它的内部实现,使用者只需要知道如何使用即可.封装使得对象之间的耦合变松散,对象之间只通过暴露的API接口来通信。
-
封装实现最常见的就是jQuery、Zepto、Lodash这类JS封装库中,用户在使用的时候并不关心其内部实现,只要它们提供了正确的功能即可
3.2 继承
继承是子类对象能够使用父类对象的数据和行为。
-
原型链继承
套路
1. 定义父类型构造函数 2. 给父类型的原型添加方法 3. 定义子类型的构造函数 4. 创建父类型的对象赋值给子类型的原型 5. 将子类型原型的构造属性设置为子类型 6. 给子类型原型添加方法 7. 创建子类型的对象: 可以调用父类型的方法关键
1. 子类型的原型为父类型的一个实例对象function Supper() { //父类型 this.superProp = 'The super prop' } //原型的数据所有的实例对象都可见 Supper.prototype.showSupperProp = function () { console.log(this.superProp) } function Sub() { //子类型 this.subProp = 'The sub prop' } // 子类的原型为父类的实例 Sub.prototype = new Supper() // 修正Sub.prototype.constructor为Sub本身 Sub.prototype.constructor = Sub Sub.prototype.showSubProp = function () { console.log(this.subProp) } // 创建子类型的实例 var sub = new Sub() // 调用父类型的方法 sub.showSubProp() // 调用子类型的方法 sub.showSupperProp()
-
借用构造函数继承
套路:
1. 定义父类型构造函数 2. 定义子类型构造函数 3. 在子类型构造函数中调用父类型构造关键:
- 在子类型构造函数中通用super()调用父类型构造函数
function Person(name, age) { this.name = name this.age = age } function Student(name, age, price) { Person.call(this, name, age) // this.Person(name, age) this.price = price } var s = new Student('Tom', 20, 12000) console.log(s.name, s.age, s.price)
-
原型链+借用构造函数的组合继承
利用原型链实现对父类型对象的方法继承
利用super()借用父类型构建函数初始化相同属性
function Person(name, age) { this.name = name this.age = age } Person.prototype.setName = function (name) { this.name = name } function Student(name, age, price) { Person.call(this, name, age) //得到父类型的属性 this.price = price } Student.prototype = new Person() //得到父类型的方法 Student.prototype.constructor = Student Student.prototype.setPrice = function (price) { this.price = price } var s = new Student('Tom', 12, 10000) s.setPrice(11000) s.setName('Bob') console.log(s) console.log(s.constructor)![]()
3.3 多态
JS对象多态性是与生俱来的:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果,也就是说,给不同的对象发送同一个消息时,这些对象会根据这个消息分别给出不同的反馈。
function Cat() {
}
Cat.prototype.eat = function () {
console.log("吃猫粮");
}
function Dog() {
}
Dog.prototype.eat = function () {
console.log("吃狗粮");
}
//发出同一个指令 可以执行不同的结果
function sayEat(obj) {
obj.eat();
}
sayEat(new Cat)
sayEat(new Dog)


浙公网安备 33010602011771号