javaScript - 面向对象编程
面向对象编程
OOP,即面向对象编程,是软件开发过程的主要方法之一。在 OOP 中,我们用对象和类来组织代码,以描述事物的特点以及它们可以做什么。
1. 在对象上创建方法(method)
方法/函数其实也是属性
对象可以有一个叫做 method 的特殊属性。
方法属性也就是函数。
let duck = { name: "Aflac", numLegs: 2, sayName: function() {return "The name of this duck is " + duck.name + ".";} }; duck.sayName();
2. 使用 this 关键字提高代码重用性
为什么使用this?
虽然通过duck.name是访问对象属性的有效方法,但是这里有一个陷阱。 如果变量名(如duck)发生了改变,那么引用了原始名称的任何代码都需要更新。
在一个简短的对象定义中,这并不是问题,但是如果对象有很多对其属性的引用,那么发生错误的可能性就更大了。
解决方法:我们可以使用 this 关键字来避免这一问题
let duck = { name: "Aflac", numLegs: 2, sayName: function() {return "The name of this duck is " + this.name + ".";} };
在当前的上下文环境中,this 指向与这个方法有关的 duck 对象。 如果把对象的变量名改为 mallard,那使用 this 后就没有必要在代码中找到所有指向 duck 的部分。 这样可以使得代码更具有可读性和复用性。
3. 构造函数
1)定义构造函数
Constructors 是创建对象的函数。 函数给这个新对象定义属性和行为。 可将它们视为创建的新对象的蓝图。
构造函数遵循以下惯例规则:
- 构造函数函数名的首字母大写
这是为了方便我们区分构造函数( constructors)和其他非构造函数。
- 构造函数使用 this 关键字来给它将创建的这个对象设置新的属性。 在构造函数里面,this 指向的就是它新创建的这个对象。
- 构造函数定义了属性和行为就可创建对象,不需要设置返回值。
function Bird() { this.name = "Albert"; this.color = "blue"; this.numLegs = 2; }
注意:构造函数内的 this 总是指被创建的对象。
2)使用构造函数
function Bird() { this.name = "Albert"; this.color = "blue"; this.numLegs = 2; } let blueBird = new Bird(); //使用new为构造函数创建新的对象
注意:通过构造函数创建对象的时候要使用 new 操作符。
因为只有这样,JavaScript 才知道要给 Bird 这个构造函数创建一个新的实例:blueBird。
如果不使用 new 操作符来新建对象,那么构造函数里面的 this 就无法指向新创建的这个对象实例,从而产生不可预见的错误。
现在 blueBird 这个实例就继承了Bird 构造函数的所有属性,如下:
blueBird.name;
blueBird.color;
blueBird.numLegs;
由构造函数创建的实例也和其他对象一样,它的属性可以被访问和修改:
blueBird.name = 'Elvira';
blueBird.name;
3)扩展构造函数以接收参数
以下为可以接收参数的构造函数:
function Bird(name, color) { this.name = name; this.color = color; this.numLegs = 2; } let terrier = new Bird(); terrier.name= “Jhon”;// 通过参数的形式传入terrier.name terrier.color = “Yellow”; // 通过参数的形式传入terrier.color
4)使用 instanceof 验证对象的构造函数
凡是通过构造函数创建出的新对象,这个对象都叫做这个构造函数的instance(实例)
要想验证这个事实,那就是通过 instanceof 操作符。 instanceof 允许你将对象与构造函数之间进行比较,根据对象是否由这个构造函数创建的返回 true 或者 false。 以下是一个示例:
let Bird = function(name, color) { this.name = name; this.color = color; this.numLegs = 2; } let crow = new Bird("Alexis", "black"); crow instanceof Bird; //返回true,因为验证出了crow是Bird的实例
5)自有属性(自身属性)
function Bird(name) { this.name = name;//name是自有属性 this.numLegs = 2; //numLegs是自有属性 } let duck = new Bird("Donald"); let canary = new Bird("Tweety");
自有属性是指直接在实例对象上定义的属性
而Bird对象的实例duck与canary都拥有这些属性的独立副本
下面的代码将 duck 的所有自身属性都存到一个叫作 ownProps 的数组里面(类似于创建一个实例的副本?):
let ownProps = []; for (let property in duck) { if(duck.hasOwnProperty(property)) { ownProps.push(property); } } console.log(ownProps);
6)prototype(_proto_)
返回对象类型原型的引用
适用于要对原型的所有实例删改属性,但是原型不能更改(比如是引用的函数)
class.prototype(注意::是类名,不是对象名)
以下是一个在 Bird prototype 中添加 numLegs 属性的示例:
注意:
Bird.prototype.numLegs指在Bird原型的所有实例中添加numLegs属性,并不会对原型有所影响(constructor原型也可以更改)
而Bird.numLegs则是直接对原型影响
Bird.prototype.numLegs = 2; console.log(duck.numLegs);//普通的调用即可
_proto_属性是个对象都有的属性
它不是一个规范属性,该特性已经从 Web 标准中删除,虽然一些浏览器目前仍然支持它。
__proto__属性已在ECMAScript 6语言规范中标准化,用于确保Web浏览器的兼容性,因此它未来将被支持。它已被不推荐使用, 现在更推荐使用Object.getPrototypeOf/Reflect.getPrototypeOf和Object.setPrototypeOf/Reflect.setPrototypeOf。
-------------------------------------------------------------
(duck和canary是Bird的实例)
7)更有效的给protytype添加属性
原本给对象的prototyppe属性是一个一个添加属性的
其实可以可以一次性添加属性的,相当于把对象的 prototype 设置为一个已经包含了属性的新对象。
Bird.prototype = { numLegs: 2, eat: function() { console.log("nom nom nom"); }, describe: function() { console.log("My name is " + this.name); } };
6)使用constructor属性找出所属对象
function joinBirdFraternity(candidate) { if (candidate.constructor === Bird) { return true; } else { return false; } }
注意:由于 constructor 属性可以被重写,所以最好使instanceof 方法来检查对象的类型。
6.1)验证对象与原型之间的关系
使用 isPrototypeOf 方法来验证对象与原型之间的关系
原型.属性名.isPrototypeOf(实例);
例如duck 从 Bird 构造函数那里继承了它的 prototype。
你可以使用 isPrototypeOf 方法来验证他们之间的关系:
Bird.prototype.isPrototypeOf(duck);//返回true
7)更改原型时,记得设置构造函数属性
构造函数属性即constructor:xxx,
手动设置一个新对象的原型会清除constructor 属性。
Bird.prototype = { constructor: Bird,//不要忘了添加constructor属性 numLegs: 2, eat: function() { console.log("nom nom nom"); }, describe: function() { console.log("My name is " + this.name); } };
更改原型如设置原型的prototype{}
8)Object.create(x,y)
创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
x为创建对象的原型对象
y为可选的需要传入一个对象
4. 原型链
JavaScript 中所有的对象(除了少数例外)都有自己的 prototype。 而且,对象的 prototype 本身也是一个对象。
5. Mixin
属性是可以通过继承原型来共享的。
然而,在有些情况下,继承不是最好的解决方案。继承不适用于不相关的对象。对于不相关的对象,更好的方法是使用 mixins。
mixin 允许其他对象使用函数集合。
let bird = { name: "Donald", numLegs: 2 }; let plane = { model: "777", numPassengers: 524 }; flyMixin(bird); flyMixin(plane);
这里的 flyMixin 接收了bird 和 plane 对象,然后将 fly 方法分配给了每一个对象。 现在 bird 和 plane 都有可以fly属性了。
6. 闭包(closure)
闭包能够保护对象内的属性不被外部修改
公共属性的定义就是:它可以在原型 的定义范围之外被访问和更改。
-------------------------------------------------------------
使属性私有化最简单的方法就是在构造函数中创建变量。
可以将该变量范围限定在构造函数中,而不是全局可用。
这样,属性只能由构造函数中的方法访问和更改
function Bird() { let hatchedEgg = 10;//重点还是let的作用域只在函数内 this.getHatchedEggCount = function() { return hatchedEgg; }; } let ducky = new Bird(); ducky.getHatchedEggCount();
这里的 getHatchedEggCount 是一种特权方法,因为只有它可以访问私有属性 hatchedEgg。
这是因为 hatchedEgg 是在与 getHatchedEggCount 相同的上下文中声明的。 (其实主要还是因为let的作用域仅在函数内,而return又在里面)
在 JavaScript 中,函数总是可以访问创建它的上下文。
这就叫做 closure。
7. 立即调用函数表达(IIFE)
(function () { console.log("Chirp, chirp!"); })();//函数在声明后立即执行
这是一个匿名函数表达式,立即执行并输出 Chirp, chirp!。
请注意:函数没有名称,也不存储在变量中。 函数表达式末尾的两个括号()会让它被立即执行或调用。
这种模式被叫做立即调用函数表达式(immediately invoked function expression) 或者IIFE。


浙公网安备 33010602011771号