类
Js 对象模型
JavaScript 中的对象模型是描述 JavaScript 中对象如何组织和相互作用的概念。JavaScript 是一种基于原型的语言,其对象模型基于原型继承而不是经典的基于类的继承。这意味着对象直接从其他对象继承属性,而不是从类(或构造函数)继承。以下是 JavaScript 中对象模型的主要概念:
-
原型链(Prototype Chain):JavaScript 中的每个对象都有一个原型(prototype)。原型是一个对象,其他对象可以从中继承属性。当你试图访问对象的属性时,如果该对象本身没有这个属性,JavaScript 将沿着原型链向上查找直到找到相应的属性或到达原型链的末尾(即
null)。 -
原型(Prototype):每个对象都有一个原型。原型是一个对象,它包含了对象的共享属性和方法。当你创建一个新对象时,你可以指定该对象的原型。如果没有明确指定,JavaScript 将会默认为其指定一个原型。
-
构造函数(Constructor Function):构造函数是一种特殊的函数,用于创建对象。在 JavaScript 中,你可以使用构造函数来创建对象,并且可以通过
new关键字调用构造函数。构造函数内部使用this关键字来指向新创建的对象,并且可以在构造函数内部定义对象的属性和方法。 -
实例(Instance):通过构造函数创建的对象被称为该构造函数的实例。每个实例都是一个独立的对象,它们共享构造函数的原型,但具有自己的属性值。
-
类(Class):尽管 JavaScript 是基于原型的语言,但 ES6 引入了类的概念,使得面向对象编程更加直观和易于理解。类可以看作是构造函数的语法糖,它提供了更类似于传统类的语法来定义对象和继承关系。
-
对象属性和方法:JavaScript 中的对象可以具有属性和方法。属性是对象的特征,而方法是对象的行为。你可以使用点运算符(
.)或方括号运算符([])来访问对象的属性和方法。
总的来说,JavaScript 中的对象模型是基于原型继承的,每个对象都有一个原型,并且可以通过原型链继承属性和方法。构造函数用于创建对象,而类提供了更加清晰的语法来定义对象和继承关系。
字面式声明方式
示例1
let a = 1;
let b = 'abc';
let c = [1,2,3];
let d = x => x * 2;
let obj = {
'a':100, // a: 100
b:b, // b: 'abc'
[b]:[b], // abc: [ 'abc' ]
[a]:200 // '1': 200,将1转换为字符串
}
console.log(obj) // { '1': 200, a: 100, b: 'abc', abc: [ 'abc' ] }
示例2
let a = 1;
let b = 'abc';
let c = [1,2,3];
let d = x => x * 2;
let obj = {a,b,c,d};
console.log(obj) // { a: 1, b: 'abc', c: [ 1, 2, 3 ], d: [Function: d] }
示例3
let a = 1;
let b = 'abc';
let c = [1,2,3];
let d = x => x * 2;
let obj = {
'a':100, // a: 100
b:b, // b: 'abc'
[b]:[b], // abc: [ 'abc' ]
[a]:200 // '1': 200
}
for (let k in obj) {
console.log(typeof k, k, obj[k])
}
// 键都是字符串
/*
string 1 200
string a 100
string b abc
string abc [ 'abc' ]
*/示例4
let d = x => x * 2;
let obj = {
d
}
console.log(obj) // { d: [Function: d] }
console.log(obj.d(600)) // 1200
通过函数创建类
在JavaScript中,您可以使用函数来创建类,这种模式通常被称为“构造函数”模式。您可以定义一个函数,然后使用该函数来创建新的对象实例。这种方式非常常见,特别是在ES5之前,当时JavaScript还没有原生的类语法。
以下是一个简单的示例,演示了如何使用函数创建一个简单的类:
// 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
// 通过原型添加方法
Person.prototype.sayHello = 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.sayHello(); // 输出: Hello, my name is Alice and I am 30 years old.
person2.sayHello(); // 输出: Hello, my name is Bob and I am 25 years old.
在这个示例中,Person函数充当了类的角色,它接受name和age作为参数,并将它们分配给新创建的对象的属性。然后,我们通过Person.prototype给该类添加了一个sayHello方法,该方法用于打印出对象实例的信息。最后,我们使用new关键字来创建新的Person对象实例,并调用了sayHello方法。
示例
function Point(x, y) {
this.x = x; // 对于类的实例来说,this永远指向实例自身
this.y = y;
console.log('point ~~~')
}
console.log(Point); // [Function: Point]
let p1 = new Point(2, 3); // point ~~~,构造出基于Point类型的实例
console.log(p1); // Point { x: 2, y: 3 },对象的表达方式
示例:子类继承
function Point(x, y) {
this.x = x;
this.y = y;
console.log('point ~~~')
}
// 子类继承
function Point3D(x, y, z) {
Point.call(this, x, y);
this.z = z
console.log('Point3D ~~~')
}
let p2 = new Point3D(1, 2, 3); // point ~~~ Point3D ~~~
console.log(p2); // Point3D { x: 1, y: 2, z: 3 }
通过class创建类
在JavaScript中,您也可以使用class关键字来创建类。class语法是从ES6开始引入的,它提供了一种更简洁、更易读的方式来定义类和构造函数,并且在语法上更接近传统的面向对象编程语言。
以下是使用class关键字创建类的示例:
// 定义一个类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 添加方法
sayHello() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
// 创建对象实例
const person1 = new Person("Alice", 30);
const person2 = new Person("Bob", 25);
// 调用方法
person1.sayHello(); // 输出: Hello, my name is Alice and I am 30 years old.
person2.sayHello(); // 输出: Hello, my name is Bob and I am 25 years old.
在这个示例中,我们使用class关键字定义了一个名为Person的类。类中的constructor方法用于初始化对象实例的属性。类中的其他方法则直接在类的主体中定义,不需要使用function关键字,也不需要在方法之间添加逗号。
与使用函数的方式相比,使用class语法更加清晰、易读,并且更符合传统的面向对象编程的习惯。
示例
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
showme() {
console.log(this, this.x, this.y);
}
}
let p1 = new Point(10, 20);
console.log(Point) // [class Point]
console.log(p1) // Point { x: 10, y: 20 }
p1.showme() // Point { x: 10, y: 20 } 10 20
示例:子类继承
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
showme() {
console.log(this, this.x, this.y);
}
}
class Point3D extends Point {
constructor(x, y, z) {
super(x, y);
this.z = z
}
}
let p2 = new Point3D(666, 888, 999);
console.log(p2) // Point3D { x: 666, y: 888, z: 999 }
p2.showme() // Point3D { x: 666, y: 888, z: 999 } 666 888
示例:属性优先
在JavaScript中,如果类中定义了同名的属性和方法,当您在对象实例上访问该名称时,属性会优先于方法。这意味着如果存在同名的属性和方法,访问该名称时会返回属性的值,而不是方法本身。这是因为JavaScript在查找属性或方法时会首先查找属性,然后才是方法。
以下是一个示例,演示了当类中定义了同名属性和方法时,属性优先于方法的情况:
class Example {
constructor() {
// 定义属性
this.name = "John";
}
// 定义方法
name() {
return "Method name";
}
}
const example = new Example();
// 访问同名属性和方法
console.log(example.name); // 输出: "John"
console.log(example.name()); // 输出: "Method name"
在这个示例中,类Example中定义了一个名为name的属性和一个同名的方法。当您在example对象实例上访问name时,会返回属性的值"John"。然后,当您调用example.name()时,会执行方法,并返回方法的结果"Method name"。
因此,在JavaScript中,属性会优先于同名的方法。
静态属性 & 静态方法
在JavaScript中,您可以使用static关键字来定义静态属性和静态方法。静态属性和静态方法属于类本身,而不是类的实例。这意味着您可以直接通过类来访问它们,而不需要创建类的实例。
以下是一个示例,演示了如何在类中定义静态属性和静态方法:
class Example {
// 静态属性
static staticProperty = "I am a static property";
// 静态方法
static staticMethod() {
return "I am a static method";
}
// 构造函数
constructor(name) {
this.name = name;
}
// 实例方法
sayHello() {
return `Hello, my name is ${this.name}`;
}
}
// 访问静态属性
console.log(Example.staticProperty); // 输出: "I am a static property"
// 调用静态方法
console.log(Example.staticMethod()); // 输出: "I am a static method"
// 创建对象实例
const example = new Example("John");
// 调用实例方法
console.log(example.sayHello()); // 输出: "Hello, my name is John"
在这个示例中,staticProperty是一个静态属性,您可以直接通过类来访问它。staticMethod是一个静态方法,同样可以直接通过类来调用。
静态属性和静态方法通常用于表示与整个类相关的行为或状态,而不是特定于单个实例的行为或状态。
示例:静态方法
静态方法是属于类本身而不是类的实例的方法。它们在类的定义中使用 static 关键字声明。静态方法通常用于执行与整个类相关的操作,而不是与特定实例相关的操作。您可以直接通过类来调用静态方法,而不需要先创建类的实例。
以下是一个示例,演示了如何在类中定义和使用静态方法:
class MathHelper {
// 静态方法,计算两个数字的和
static add(x, y) {
return x + y;
}
// 静态方法,计算两个数字的差
static subtract(x, y) {
return x - y;
}
}
// 调用静态方法
console.log(MathHelper.add(5, 3)); // 输出: 8
console.log(MathHelper.subtract(10, 4)); // 输出: 6
在这个示例中,add 和 subtract 方法都是静态方法。您可以直接通过 MathHelper 类来调用这些方法,而不需要先创建类的实例。这使得它们非常适合执行通用的功能,例如数学运算或其他与类本身相关的操作。
this问题
this 与 globalThis
this 关键字在 JavaScript 中是一个非常重要的概念,它通常指向当前执行代码的对象。但是,this 的指向取决于代码执行的上下文,可能会在不同的情况下指向不同的对象。
另一方面,globalThis 是一个全局对象,它提供了在任何 JavaScript 环境中访问全局对象的标准方式。它的作用是提供一个标准化的方式来访问全局对象,而不受宿主环境的限制。在浏览器中,globalThis 指向 window 对象;在 Node.js 中,globalThis 指向全局对象 global。
下面是一个简单的示例来说明它们之间的区别:
console.log(this === globalThis); // 在浏览器中,可能为 false;在 Node.js 中,为 true
function testFunction() {
console.log(this === globalThis); // 在浏览器和 Node.js 中,为 false
}
testFunction();在上面的示例中,this === globalThis 的结果取决于代码的执行环境。在浏览器环境中,全局的 this 可能指向 window 对象,而 globalThis 也指向 window 对象,所以它们相等。但在函数内部,this 的值会根据函数的调用方式而变化,通常不会指向全局对象,因此 this === globalThis 的结果为 false。
在 Node.js 环境中,globalThis 和 this 都指向全局对象 global,所以 this === globalThis 的结果为 true。
总的来说,globalThis 提供了一种标准的方式来访问全局对象,而 this 关键字则是在不同的执行上下文中动态确定的。