欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

044_定义类或对象

发布时间:2025/4/17 编程问答 21 豆豆
生活随笔 收集整理的这篇文章主要介绍了 044_定义类或对象 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

1. 纯构造函数方式

1.1. 使用构造函数创建对象, 第一步选择类名, 即构造函数的名字。根据惯例, 这个名字的首字母大写, 以使它与首字母通常是小写的函数名分开。请考虑下面的例子:

function Car(color, doors, mpg) {this.color = color;this.doors = doors;this.mpg = mpg;this.showColor = function() {alert(this.color);}; }var car1 = new Car("red", 4, 23); var car2 = new Car("blue", 3, 25);

1.2. 上例使用纯构造函数方式创建对象的问题: 构造函数会重复生成函数, 为每个对象都创建独立的函数版本。

2. 纯原型方式

2.1. 该方式利用了对象的prototype属性, 可以把它看成创建新对象所依赖的原型。

2.2. 这里, 首先用空构造函数来设置类名。然后所有的属性和方法都被直接赋予prototype属性。我们重写了前面的例子, 代码如下:

function Car() {}Car.prototype.color = "blue"; Car.prototype.doors = 4; Car.prototype.mpg = 25; Car.prototype.showColor = function() {alert(this.color); };var car1 = new Car(); var car2 = new Car();

2.3. 原型方式的问题

2.3.1. 首先, 这个构造函数没有参数。使用原型方式, 不能通过给构造函数传递参数来初始化属性的值, 因此car1和car2的color属性都等于"blue", doors属性都等于4, mpg属性都等于 25。这意味着必须在对象创建后才能改变属性的默认值, 这点很令人讨厌, 但还没完。真正的问题出现在属性指向的是对象, 而不是函数时。函数共享不会造成问题, 但对象却很少被多个实例共享。请思考下面的例子:

function Car() {}Car.prototype.color = "blue"; Car.prototype.doors = 4; Car.prototype.mpg = 25; Car.prototype.drivers = new Array("Mike", "John"); Car.prototype.showColor = function() {alert(this.color); };var car1 = new Car(); var car2 = new Car();car1.drivers.push("Bill");alert(car1.drivers); // 输出"Mike, John, Bill" alert(car2.drivers); // 输出"Mike, John, Bill"

2.3.2. 上面的代码中, 属性drivers是指向Array对象的指针, 该数组中包含两个名字"Mike"和"John"。由于drivers是引用值, Car的两个实例都指向同一个数组。这意味着给car1.drivers添加值"Bill", 在car2.drivers 中也能看到。输出这两个指针中的任何一个, 结果都是显示字符串"Mike, John, Bill"。

3. 混合的构造函数和原型方式

3.1. 联合使用构造函数和原型方式, 就可像用其他程序设计语言一样创建对象。这种概念非常简单, 即用构造函数定义对象的所有非函数属性, 用原型方式定义对象的函数属性(方法)。结果是, 所有函数都只创建一次, 而每个对象都具有自己的对象属性实例。

3.2. 我们重写了前面的例子, 代码如下:

function Car(color, doors, mpg) {this.color = color;this.doors = doors;this.mpg = mpg;this.drivers = new Array("Mike", "John"); }Car.prototype.showColor = function() {alert(this.color); };var car1 = new Car("red", 4, 23); var car2 = new Car("blue", 3, 25);car1.drivers.push("Bill");alert(car1.drivers); // 输出"Mike, John, Bill" alert(car2.drivers); // 输出"Mike, John"

3.3. 这种方式是JavaScript采用的主要方式, 它具有其他方式的特性, 却没有他们的副作用。

4. 动态原型方法

4.1. 动态原型方法的基本想法与混合的构造函数和原型方式相同, 即在构造函数内定义非函数属性, 而函数属性则利用原型属性定义。唯一的区别是赋予对象方法的位置。下面是用动态原型方法重写的Car类:

function Car(color, doors, mpg) {this.color = color;this.doors = doors;this.mpg = mpg;this.drivers = new Array("Mike", "John");if (typeof Car._initialized == "undefined") {Car.prototype.showColor = function() {alert(this.color);};Car._initialized = true;} }

4.2. 直到检查typeof Car._initialized是否等于"undefined"之前, 这个构造函数都未发生变化。这行代码是动态原型方法中最重要的部分。如果这个值未定义, 构造函数将用原型方式继续定义对象的方法, 然后把 Car._initialized设置为true。如果这个值定义了(它的值为true时, typeof的值为 Boolean), 那么就不再创建该方法。简而言之, 该方法使用标志(_initialized)来判断是否已给原型赋予了任何方法。该方法只创建并赋值一次, 传统的OOP开发者会高兴地发现, 这段代码看起来更像其他语言中的类定义了。

5. 字符串连接

5.1. 对象令人感兴趣的一点是用它们解决问题的方式。JavaScript中最常见的一个问题是字符串连接的性能。与其他语言类似, JavaScript的字符串是不可变的, 即它们的值不能改变。请考虑下面的代码:

var str = "hello "; str += "world";

5.2. 实际上, 这段代码在幕后执行的步骤如下:

5.2.1. 创建存储"hello "的字符串。

5.2.2. 创建存储"world"的字符串。

5.2.3. 创建存储连接结果的字符串。

5.2.4. 把str的当前内容复制到结果中。

5.2.5. 把"world"复制到结果中。

5.2.6. 更新str, 使它指向结果。

5.3. 每次完成字符串连接都会执行步骤5.2.2到5.2.6, 使得这种操作非常消耗资源。如果重复这一过程几百次, 甚至几千次, 就会造成性能问题。解决方法是用Array对象存储字符串, 然后用join() 方法(参数是空字符串)创建最后的字符串。想象用下面的代码代替前面的代码:

var arr = new Array(); arr[0] = "hello "; arr[1] = "world"; var str = arr.join("");

5.4. 这样, 无论数组中引入多少字符串都不成问题, 因为只在调用join()方法时才会发生连接操作。此时, 执行的步骤如下:

5.4.1. 创建存储结果的字符串。

5.4.2. 把每个字符串复制到结果中的合适位置。

5.5. 虽然这种解决方案很好, 但还有更好的方法。问题是, 这段代码不能确切反映出它的意图。要使它更容易理解, 可以用StringBuffer类打包该功能:

function StringBuffer () {this._strings_ = new Array(); }StringBuffer.prototype.append = function(str) {this._strings_.push(str); };StringBuffer.prototype.toString = function() {return this._strings_.join(""); };

5.6. 这段代码首先要注意的是strings属性, 本意是私有属性。它只有两个方法, 即append()和toString()方法。append()方法有一个参数, 它把该参数附加到字符串数组中, toString()方法调用数组的join 方法, 返回真正连接成的字符串。要用StringBuffer对象连接一组字符串, 可以用下面的代码:

var buffer = new StringBuffer(); buffer.append("hello "); buffer.append("world"); var result = buffer.toString();

5.7. 例子

5.7.1. 代码

<!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="utf-8" /><title>字符串连接</title> </head> <body><script type="text/javascript">function StringBuffer () {this._strings_ = new Array();}StringBuffer.prototype.append = function(str) {this._strings_.push(str);};StringBuffer.prototype.toString = function() {return this._strings_.join("");};var buffer = new StringBuffer();buffer.append('<h1>华为Mate40 RS 5G保时捷限量版手机</h1>')buffer.append('<h3>商品介绍</h3>');buffer.append('品牌: 华为(HUAWEI)');buffer.append('<br />');buffer.append('商品编号: 10023728787945');buffer.append('&nbsp;&nbsp;&nbsp;&nbsp;');buffer.append('商品毛重: 300.00g');buffer.append('&nbsp;&nbsp;&nbsp;&nbsp;');buffer.append('运行内存: 12GB');buffer.append('&nbsp;&nbsp;&nbsp;&nbsp;');buffer.append('机身存储: 512GB');buffer.append('<br />');buffer.append('存储卡: NM存储卡');buffer.append('&nbsp;&nbsp;&nbsp;&nbsp;');buffer.append('摄像头数量: 后置五摄');buffer.append('&nbsp;&nbsp;&nbsp;&nbsp;');buffer.append('后摄主摄像素: 5000万像素');buffer.append('&nbsp;&nbsp;&nbsp;&nbsp;');buffer.append('前摄主摄像素:1300万像素');buffer.append('<br />');buffer.append('主屏幕尺寸(英寸): 6.67英寸');buffer.append('&nbsp;&nbsp;&nbsp;&nbsp;');buffer.append('分辨率: 全高清FHD+');buffer.append('&nbsp;&nbsp;&nbsp;&nbsp;');buffer.append('充电器: 11V/6A;10V/4A');buffer.append('&nbsp;&nbsp;&nbsp;&nbsp;');buffer.append('热点: 5G');buffer.append('<br />');buffer.append('操作系统: Android(安卓)');buffer.append('&nbsp;&nbsp;&nbsp;&nbsp;');buffer.append('价格: ¥ 19288.00');var result = buffer.toString();document.write(result);</script> </body> </html>

5.7.2. 效果图

总结

以上是生活随笔为你收集整理的044_定义类或对象的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。