构造函数
在 JavaScript 中,构造函数是一种特殊的函数,用于创建和初始化一个新的对象。在 JavaScript 中,我们使用构造函数来定义对象类型,并创建基于该对象类型的新实例。
使用构造函数的场景:
- 创建多个具有相同属性和方法的对象
- 对象需要使用动态生成的数据进行初始化,比如从数据库中读取数据
- 对象需要进行一些预处理或者初始化操作
使用构造函数的步骤如下:
- 定义一个构造函数,并在构造函数内部使用
this
关键字来引用新创建的对象,并为该对象添加属性和方法 - 通过
new
关键字来创建对象的实例,可以传递参数给构造函数,以便根据不同的参数执行不同的初始化操作 - 使用实例对象的属性和方法
// 定义一个构造函数
function Person(name, age) {
this.name = name;
this.age = age;
// 在构造函数内部定义一个方法
this.sayHello = function() {
console.log('Hello, my name is ' + this.name + ' and I am ' + this.age + ' years old.');
};
} // 创建一个 Person 对象的实例
var person1 = new Person('Alice', 30);
var person2 = new Person('Bob', 25); // 使用实例对象的属性和方法 console.log(person1.name); // 输出:Alice person2.sayHello(); // 输出:Hello, my name is Bob and I am 25 years old.
new的实例化过程
在 JavaScript 中,使用 new
关键字来创建对象的实例时,会执行以下步骤:
- 创建一个新的空对象
- 将该空对象的原型链接到构造函数的
prototype
属性上 - 执行构造函数,并将
this
关键字绑定到新创建的空对象上 - 如果构造函数有返回值,并且返回值是一个对象,则将该对象作为
new
表达式的返回值;否则返回新创建的空对象
简单说来,当我们使用 new
关键字来创建对象的实例时,实际上就是通过构造函数来初始化一个新的对象。在初始化过程中,系统会自动执行一系列步骤,包括创建一个新的空对象并将其绑定到构造函数上,执行构造函数并初始化对象的属性和方法等。
// 定义一个构造函数
function Person(name, age) {
this.name = name;
this.age = age;
// 在构造函数内部定义一个方法
this.sayHello = function() {
console.log('Hello, my name is ' + this.name + ' and I am ' + this.age + ' years old.');
};
} // 使用 new 关键字创建对象实例
var person = new Person('Alice', 30); // 调用对象的方法
person.sayHello(); // 输出:Hello, my name is Alice and I am 30 years old.
实例成员
实例对象(Instance Objects)是通过构造函数创建的对象,而实例成员(Instance Members)是指属于实例对象的属性和方法。
实例对象是根据构造函数创建的具体对象,每个实例对象都有自己独立的属性值,并且可以调用属于它们的方法。这些属性和方法被称为实例成员,因为它们属于实例对象的特定实例。
function Person(name, age) {
// 实例属性
this.name = name;
this.age = age;
}
// 实例方法
Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};
// 创建两个实例对象
var person1 = new Person('Alice', 30);
var person2 = new Person('Bob', 25);
person1.greet(); // 调用实例方法
person2.greet();
console.log(person1.name); // 访问实例属性
console.log(person2.age);
静态成员
静态成员(Static Members)通常是通过在构造函数本身上定义属性和方法,而不是在其原型上定义。这样定义的成员不会被每个对象实例复制,而是共享给所有对象实例。静态成员可以通过构造函数本身来访问和调用,而不是通过对象实例。
function MathUtility() {}
// 静态属性
MathUtility.PI = 3.14159;
// 静态方法
MathUtility.calculateCircleArea = function(radius) {
return Math.PI * radius * radius;
};
console.log(MathUtility.PI); // 访问静态属性
var area = MathUtility.calculateCircleArea(5); // 调用静态方法
console.log(area);
包装对象
包装对象(Wrapper Objects)是指基本数据类型(如字符串、数字、布尔值)对应的对象形式。当我们尝试在基本数据类型上调用对象方法时,JavaScript会临时将基本数据类型转换为对应的包装对象,以便调用该方法,然后再将其转换回基本数据类型。
Object
Object.keys()、Object.values() :分别用于获取对象的键、值。
const obj = { a: 1, b: 2, c: 3 };
console.log(Object.keys(obj)); // ['a', 'b', 'c']
console.log(Object.values(obj)); // [1, 2, 3]
console.log(Object.entries(obj)); // [['a', 1], ['b', 2], ['c', 3]]
Object.assign()
方法用于将一个或多个源对象的所有可枚举属性复制到目标对象,并返回目标对象。这个方法可以用来实现对象的浅拷贝。
// 创建目标对象
let target = { };
// 创建源对象
let source1 = { b: 3, c: 4 };
// 将源对象的属性复制到目标对象
Object.assign(target, source1);
// 输出目标对象
console.log(target);
Array
reduce()用于对数组中的每个元素执行指定的回调函数,并从左到右累积计算结果。
const numbers = [1, 2, 3, 4, 5];
// 计算数组中所有元素的和
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 输出 15,即 1 + 2 + 3 + 4 + 5 的和
join(separator)
方法用于将数组中的所有元素转换为字符串,并使用指定的分隔符来连接这些字符串。它接收一个参数 separator
,表示要用作分隔符的字符串,默认为逗号。调用 join()
方法会返回一个由数组元素组成的字符串,元素之间用指定的分隔符分隔。
const fruits = ['apple', 'banana', 'orange'];
const result = fruits.join(', ');
console.log(result); // 输出 "apple, banana, orange"
every(callbackFn)
方法用于判断数组中的每个元素是否都通过了指定函数的测试。它接收一个参数 callbackFn
,表示要对每个数组元素执行的测试函数,这个函数返回 true
或 false
。every()
方法会遍历数组的每个元素,直到有一个元素未通过测试,则返回 false
;如果所有元素都通过了测试,则返回 true
。
const numbers = [2, 4, 6, 8, 10];
const allEven = numbers.every(num => num % 2 === 0);
console.log(allEven); // 输出 true,因为数组中所有元素都是偶数
find()
方法用于查找数组中满足提供的测试函数的第一个元素。它接收一个参数 callbackFn
,表示要对每个数组元素执行的测试函数,这个函数返回 true
或 false
。find()
方法会遍历数组的每个元素,直到找到第一个满足条件的元素,然后返回该元素;如果没有找到满足条件的元素,则返回 undefined
。
const numbers = [5, 12, 8, 130, 44];
const found = numbers.find(element => element > 10);
console.log(found); // 输出 12,因为它是数组中第一个大于 10 的元素
构造函数实现封装及问题
构造函数可以通过闭包的特性来实现封装,将一些属性和方法私有化,只暴露必要的接口给外部。
function Person(name, age) {
var _name = name; // 私有属性
var _age = age; // 私有属性
// 公有方法
this.getName = function() {
return _name;
};
this.getAge = function() {
return _age;
};
}
var person1 = new Person("Alice", 30);
console.log(person1.getName()); // "Alice"
console.log(person1._name); // undefined,私有属性无法直接访问
使用构造函数实现封装可能会存在浪费内存的问题。
在 JavaScript 中,每个通过构造函数创建的对象都会有一组独立的属性和方法,这些属性和方法都是在对象实例化时在内存中被分配的。如果我们需要创建大量的对象,就需要分配大量的内存空间来存储这些属性和方法,这将会对性能产生影响,尤其是在低端设备上或者移动端。
此外,由于每个对象都有自己独立的属性和方法,这些属性和方法都需要在内存中占用存储空间,因此会导致内存的浪费。
为了避免浪费内存,可以考虑使用原型链来共享对象的属性和方法。通过把对象的属性和方法定义在原型对象上,所有的对象实例都可以共享这些属性和方法,从而减少内存的使用。这种方式被称为原型继承,它可以避免对象实例化时重复定义属性和方法,达到节省内存的目的。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.getName = function() {
return this.name;
};
Person.prototype.getAge = function() {
return this.age;
};
var person1 = new Person("Alice", 30);
console.log(person1.getName()); // "Alice"
console.log(person1.age); // 30,可以直接访问公有属性
原型
在 JavaScript 中,每个对象都有一个原型(prototype),它是一个指向另一个对象的引用。原型对象包含了一些共享的属性和方法,可以被它的所有实例对象所共享。
当我们创建一个新对象时,它会自动继承其构造函数的 prototype 属性。通过这种方式,我们可以将方法和属性添加到一个构造函数的 prototype 中,从而使得这个构造函数创建的所有实例对象都能够共享这些方法和属性。
constrctor属性
在 JavaScript 中,每个对象都有一个特殊的内置属性 constructor
,它引用创建对象的构造函数。这个属性指向用于创建对象的构造函数本身。
当我们使用构造函数创建一个对象时,该对象会自动获得一个名为 constructor
的属性,指向用于创建它的构造函数。这样我们就可以通过对象的 constructor
属性来获取创建该对象的构造函数,并且可以使用这个属性来检查对象的类型。
对象原型__proto__
__proto__
是 JavaScript 中对象的一个内置属性,它是一个指向该对象的原型(prototype)的引用。这个属性允许我们直接访问和操作对象的原型。
const animal = {
type: 'Animal',
sound: function() {
console.log('Some sound');
}
};
const cat = {
name: 'Fluffy',
__proto__: animal // 设置 cat 对象的原型为 animal
};
console.log(cat.name); // 输出 "Fluffy"
cat.sound(); // 输出 "Some sound"
console.log(cat.__proto__); // 输出 { type: 'Animal', sound: [Function: sound] }
原型继承
在 JavaScript 中,原型继承是一种实现对象之间继承关系的方式。每个对象都有一个原型对象,可以通过原型链来实现属性和方法的继承。
在原型继承中,一个对象可以通过指定另一个对象作为其原型,从而继承该原型对象的属性和方法。这样,子对象就可以共享父对象的属性和方法,实现了代码的重用和组织。
// 定义一个动物对象作为原型
const animal = {
type: 'Animal',
sound: function() {
console.log('Some sound');
}
};
// 创建一个猫对象,并将动物对象设置为其原型
const cat = Object.create(animal);
cat.name = 'Fluffy';
// 调用猫对象的方法
console.log(cat.name); // 输出 "Fluffy"
cat.sound(); // 输出 "Some sound"
原型链
原型链(Prototype Chain)是 JavaScript 中实现对象继承的一种机制。每个对象都有一个原型对象,而原型对象也可以有自己的原型对象,这样就形成了一个链式结构,被称为原型链。
当我们通过对象访问一个属性或方法时,如果该对象本身没有定义这个属性或方法,JavaScript 引擎会沿着原型链向上查找,直到找到为止。这个过程会一直持续到达原型链的顶端,即 Object.prototype,如果在整个原型链中都没有找到对应的属性或方法,那么返回 undefined。
// 定义一个原型对象
const animal = {
type: 'Animal',
sound: function() {
console.log('Some sound');
}
};
// 创建一个新对象,并将原型设置为 animal
const cat = Object.create(animal);
cat.name = 'Fluffy';
// 访问 cat 对象的属性和方法
console.log(cat.name); // 输出 "Fluffy"
cat.sound(); // 输出 "Some sound"
// 继续向上查找
console.log(cat.toString()); // 输出 "[object Object]"