

**JavaScript 类(class)**

**类是用于创建对象的模板。**

我们使用 class 关键字来创建一个类，类体在一对大括号 **{}** 中，我们可以在大括号 **{}** 中定义类成员的位置，如方法或构造函数。

每个类中包含了一个特殊的方法 **constructor()**，它是类的构造函数，这种方法用于创建和初始化一个由 **class** 创建的对象。

constructor() 方法是一种特殊的方法(构造方法)，用于创建和初始化在类中创建的对象。

创建对象时会自动调用构造方法 constructor()。

如果没有显式指定构造方法，则会添加默认的 constructor 方法。

如果不指定一个构造函数 (constructor) 方法，则使用一个默认的构造函数 (constructor)。

在一个构造方法中可以使用 super 关键字来调用一个父类的构造方法。

实例：

````


```
constructor(parameters)
```

创建一个类的语法格式如下：

class ClassName { constructor() { ... } }

class Runoob{

constructor(name,url)
{
this.name=name;
this.url=url;

}

}
````



以上实例创建了一个类，名为 "Runoob"。

类中初始化了两个属性： "name" 和 "url"。



**使用类**

定义好类后，我们就可以使用 **new** 关键字来创建对象：

```js
class Runoob {
  constructor(name, url) {
    this.name = name;
    this.url = url;
  }
}
 
let site = new Runoob("菜鸟教程",  "https://www.runoob.com");
```




创建对象时会自动调用构造函数方法 **constructor()**。

**类表达式**

类表达式是定义类的另一种方法。类表达式可以命名或不命名。命名类表达式的名称是该类体的局部名称。

```js
// 未命名/匿名类
let Runoob = class {
  constructor(name, url) {
    this.name = name;
    this.url = url;
  }
};
console.log(Runoob.name);
// output: "Runoob"
 
// 命名类
let Runoob = class Runoob2 {
  constructor(name, url) {
    this.name = name;
    this.url = url;
  }
};
console.log(Runoob.name);
// 输出: "Runoob2"
```



构造方法

构造方法是一种特殊的方法：

- 构造方法名为 constructor()。
- 构造方法在创建新对象时会自动执行。
- 构造方法用于初始化对象属性。
- 如果不定义构造方法，JavaScript 会自动添加一个空的构造方法。

**类的方法**

我们使用关键字 **class** 创建一个类，可以添加一个 **constructor()** 方法，然后添加任意数量的方法。

class ClassName { constructor() { ... } method_1() { ... } method_2() { ... } method_3() { ... } }

以下实例我们创建一个 "age" 方法，用于返回网站年龄：

```js
class Runoob {
  constructor(name, year) {
    this.name = name;
    this.year = year;
  }
  age() {
    let date = new Date();
    return date.getFullYear() - this.year;
  }
}
 
let runoob = new Runoob("菜鸟教程", 2018);
document.getElementById("demo").innerHTML =
"菜鸟教程 " + runoob.age() + " 岁了。";
```

我们还可以向类的方法发送参数：

**实例**

```js
class Runoob {
  constructor(name, year) {
    this.name = name;
    this.year = year;
  }
  age(x) {
    return x - this.year;
  }
}
 
let date = new Date();
let year = date.getFullYear();
 
let runoob = new Runoob("菜鸟教程", 2020);
document.getElementById("demo").innerHTML=
"菜鸟教程 " + runoob.age(year) + " 岁了。";
```





### 类的继承

JavaScript 类(class) extends 关键字

```js
class Site {
  constructor(name) {
    this.sitename = name;
  }
  present() {
    return '我喜欢' + this.sitename;
  }
}
 
class Runoob extends Site {
  constructor(name, age) {
    super(name);
    this.age = age;
  }
  show() {
    return this.present() + ', 它创建了 ' + this.age + ' 年。';
  }
}
 
let noob = new Runoob("菜鸟教程", 5);
document.getElementById("demo").innerHTML = noob.show();
```



extends 关键字用于创建一个类，该类是另一个类的子类。

子类继承了另一个类的所有方法。

继承对于代码可重用性很有用：在创建新类时重用现有类的属性和方法。

super() 方法引用父类的构造方法。

通过在构造方法中调用 super() 方法，我们调用了父类的构造方法，这样就可以访问父类的属性和方法。







static关键字

以下实例创建的类 "Runoob"，并创建静态方法 hello() :

```js
class Runoob {
  constructor(name) {
    this.name = name;
  }
  static hello() {
    return "Hello!!";
  }
}
 
let noob = new Runoob("菜鸟教程");
 
// 可以在类中调用 'hello()' 方法
document.getElementById("demo").innerHTML = Runoob.hello();
 
// 不能通过实例化后的对象调用静态方法
// document.getElementById("demo").innerHTML = noob.hello();
// 以上代码会报错
```

定义和用法

类（class）通过 static 关键字定义静态方法。

静态方法调用直接在类上进行，不能在类的实例上调用。

静态方法通常用于创建实用程序函数。

语法

```
static methodName()
```



**严格模式 "use strict"**

类声明和类表达式的主体都执行在严格模式下。比如，构造函数，静态方法，原型方法，getter 和 setter 都在严格模式下执行。

如果你没有遵循严格模式，则会出现错误：

```js
age() {
    // date = new Date();  // 错误
    let date = new Date(); // 正确
    return date.getFullYear() - this.year;
  }
```





super关键字

```js
class Site {
  constructor(name) {
    this.sitename = name;
  }
  present() {
    return '我喜欢' + this.sitename;
  }
}
 
class Runoob extends Site {
  constructor(name, age) {
    super(name);
    this.age = age;
  }
  show() {
    return this.present() + ', 它创建了 ' + this.age + ' 年。';
  }
}
 
let noob = new Runoob("菜鸟教程", 5);
document.getElementById("demo").innerHTML = noob.show();
```





定义和用法

super 关键字用于访问和调用一个对象的父对象上的函数。。

在构造函数中使用时，super关键字将单独出现，并且必须在使用 this 关键字之前使用。super 关键字也可以用来调用父对象上的函数。

语法

```js
super(arguments);  // 调用父构造函数
  super.parentMethod(arguments);  // 调用父方法
```

**super()** 方法引用父类的构造方法。

通过在构造方法中调用 **super()** 方法，我们调用了父类的构造方法，这样就可以访问父类的属性和方法。

继承对于代码可复用性很有用。

JavaScript 并没有像其他编程语言一样具有传统的类，而是基于原型的继承模型。

ES6 引入了类和 **class** 关键字，但底层机制仍然基于原型继承。



**使用原型链继承**

在下面实例中，Animal 是一个基类，Dog 是一个继承自 Animal 的子类，Dog.prototype 使用 Object.create(Animal.prototype) 来创建一个新对象，它继承了 Animal.prototype 的方法和属性，通过将 Dog.prototype.constructor 设置为 Dog，确保继承链上的构造函数正确。

```js
function Animal(name) {
  this.name = name;
}
 
Animal.prototype.eat = function() {
  console.log(this.name + " is eating.");
};
 
function Dog(name, breed) {
  Animal.call(this, name);
  this.breed = breed;
}
 
// 建立原型链，让 Dog 继承 Animal
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
 
Dog.prototype.bark = function() {
  console.log(this.name + " is barking.");
};
 
var dog = new Dog("Buddy", "Labrador");
dog.eat();  // 调用从 Animal 继承的方法
dog.bark(); // 调用 Dog 的方法
```



使用 ES6 类继承

ES6 引入了 class 关键字，使得定义类和继承更加清晰，extends 关键字用于建立继承关系，super 关键字用于在子类构造函数中调用父类的构造函数。



```js
class Animal {
  constructor(name) {
    this.name = name;
  }
 
  eat() {
    console.log(this.name + " is eating.");
  }
}
 
class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
 
  bark() {
    console.log(this.name + " is barking.");
  }
}
 
const dog = new Dog("Buddy", "Labrador");
dog.eat();
dog.bark();
```



JavaScript 类继承使用 extends 关键字。

继承允许我们依据另一个类来定义一个类，这使得创建和维护一个应用程序变得更容易。

**super()** 方法用于调用父类的构造函数。

当创建一个类时，您不需要重新编写新的数据成员和成员函数，只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为**基类（父类）**，新建的类称为**派生类（子类）**。

继承代表了 **is a** 关系。例

![img](https://www.runoob.com/wp-content/uploads/2015/05/cpp-inheritance-2020-12-15-1.png)



```js
// 基类
class Animal {
    // eat() 函数
    // sleep() 函数
};
 
 
//派生类
class Dog extends Animal {
    // bark() 函数
};
```

类中我们可以使用 getter 和 setter 来获取和设置值，getter 和 setter 都需要在严格模式下执行。

getter 和 setter 可以使得我们对属性的操作变的很灵活。

类中添加 getter 和 setter 使用的是 get 和 set 关键字。

注意：即使 getter 是一个方法，当你想获取属性值时也不要使用括号。

getter/setter 方法的名称不能与属性的名称相同，在本例中属名为 sitename。

很多开发者在属性名称前使用下划线字符 _ 将 getter/setter 与实际属性分开：

以下实例使用下划线 _ 来设置属性，并创建对应的 getter/setter 方法：

```js
class Runoob {
  constructor(name) {
    this._sitename = name;
  }
  get sitename() {
    return this._sitename;
  }
  set sitename(x) {
    this._sitename = x;
  }
}
 
let noob = new Runoob("菜鸟教程");
 
document.getElementById("demo").innerHTML = noob.sitename;
```



## 提升

函数声明和类声明之间的一个重要区别在于, 函数声明会提升，类声明不会。

你首先需要声明你的类，然后再访问它，否则类似以下的代码将抛出 ReferenceError：

- **getter** 是一种获得属性值的方法，**setter** 是一种设置属性值的方法
- **getter** 负责查询值，它不带任何参数，**setter** 则负责设置键值，值是以参数的形式传递，在他的函数体中，一切的 **return** 都是无效的。
- **get/set** 访问器不是对象的属性，而是属性的特性，特性只有内部才用，因此在 JavaScript 中不能直接访问他们，为了表示特性是内部值用两对中括号括起来表示如 **[[Value]]**。

静态方法是使用 static 关键字修饰的方法，又叫类方法，属于类的，但不属于对象，在实例化对象之前可以通过 **类名.方法名** 调用静态方法。

静态方法不能在对象上调用，只能在类中调用。

```js

class Runoob {
  constructor(name) {
    this.name = name;
  }
  static hello() {
    return "Hello!!";
  }
}
 
let noob = new Runoob("菜鸟教程");
 
// 可以在类中调用 'hello()' 方法
document.getElementById("demo").innerHTML = Runoob.hello();
 
// 不能通过实例化后的对象调用静态方法
// document.getElementById("demo").innerHTML = noob.hello();
// 以上代码会报错
```









### 对象字面量中的方法

javascript

```js
var person = {
    fullName : function() {
        return this.firstName + " " + this.lastName;
    }
};
```



### 类中的方法

javascript

```js
class Runoob {
  age() {
    let date = new Date();
    return date.getFullYear() - this.year;
  }
}
```

JavaScript 在 ES6（2015年）之前，定义对象的唯一方式就是字面量或构造函数。对象中的方法必须写成 `键: 函数表达式` 的形式：

javascript

```
// ES5 及之前
var obj = {
  method: function() {
    // ...
  }
};
```

ES6 引入了**方法定义简写**（method definition shorthand），允许在对象字面量中也使用简写形式：

javascript

```
// ES6 对象字面量也可以这样写
var person = {
    fullName() {  // 注意：不需要 function 关键字和冒号
        return this.firstName + " " + this.lastName;
    }
};
```

类是 ES6 新增的**语法糖**，它的方法定义就采用了这种简写形式。实际上，类的方法定义等价于在原型对象上定义方法：

javascript

```js
// 类写法
class Runoob {
  age() { }
}

// ES5 构造函数 + 原型方法
function Runoob() {
  // constructor 内容
}

// age 方法定义在原型上
Runoob.prototype.age = function() { };

// 并且这个方法被标记为不可枚举
Object.defineProperty(Runoob.prototype, 'age', {
  enumerable: false,     // 关键：不可枚举
  writable: true,
  configurable: true
});
```



关键区别：不是普通 prototype 赋值

如果只是简单地写 `Runoob.prototype.age = function() {}`，**不完全等价**，因为类方法默认是**不可枚举**的：

```js
// ES5 传统写法 - 可枚举
const obj = {
  age2: function() {}  // ✅ 可枚举
};

// ES6 class - 不可枚举
class Runoob {
  age() {}  // ❌ 不可枚举
}


// 验证
class Runoob {
  age() { }
}

function Runoob2() { }
Runoob2.prototype.age = function() { };

// 类方法不可枚举
console.log(Object.keys(Runoob.prototype));        // []
console.log(Object.getOwnPropertyNames(Runoob.prototype));  // ['constructor', 'age']

// 普通 prototype 赋值可枚举
console.log(Object.keys(Runoob2.prototype));       // ['age']
```



| 特性         | 对象字面量（ES5风格）   | 对象字面量（ES6简写） | 类方法        |
| :----------- | :---------------------- | :-------------------- | :------------ |
| 语法         | `method: function() {}` | `method() {}`         | `method() {}` |
| 出现时间     | ES5 及之前              | ES6                   | ES6           |
| `super` 支持 | ❌                       | ❌（特殊场景除外）     | ✅             |
| 可枚举性     | 可枚举                  | 可枚举                | 不可枚举      |
| 本质         | 普通函数属性            | 普通函数属性          | 原型方法      |



JavaScript 是**基于原型（Prototype-based）**的面向对象，它支持多种创建对象的方式：

#### 方式一：对象字面量（直接创建，不需要类）

javascript

```
// 直接创建对象，不需要类！
let person = {
  name: "Alice",
  sayHello: function() {
    console.log("Hello, I'm " + this.name);
  }
};

person.sayHello();  // 完全可行
```



#### 方式二：工厂函数

javascript

```
function createPerson(name, age) {
  return {
    name: name,
    age: age,
    sayHello() {
      console.log(`Hello, I'm ${this.name}`);
    }
  };
}

let person = createPerson("Alice", 25);  // 不需要 new，不需要类
```



#### 方式三：构造函数（ES5 方式）

javascript

```
function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.sayHello = function() {
  console.log(`Hello, I'm ${this.name}`);
};

let person = new Person("Alice", 25);  // 用构造函数创建
```



#### 方式四：Object.create()（基于原型）

javascript

```
let personPrototype = {
  sayHello() {
    console.log(`Hello, I'm ${this.name}`);
  }
};

let person = Object.create(personPrototype);
person.name = "Alice";  // 直接基于原型创建对象
```



#### 方式五：ES6 类（语法糖）

javascript

```
class Person {
  constructor(name) {
    this.name = name;
  }
  sayHello() {
    console.log(`Hello, I'm ${this.name}`);
  }
}

let person = new Person("Alice");
```





### JS对象



JavaScript 提供多个内建对象，比如 String、Date、Array 等等。 对象只是带有属性和方法的特殊数据类型。

- 布尔型可以是一个对象。
- 数字型可以是一个对象。
- 字符串也可以是一个对象
- 日期是一个对象
- 数学和正则表达式也是对象
- 数组是一个对象
- 甚至函数也可以是对象





对象只是一种特殊的数据。对象拥有**属性**和**方法**。

访问对象的属性

属性是与对象相关的值。

访问对象属性的语法是：

```js
objectName.propertyName
```

访问对象的方法

方法是能够在对象上执行的动作。

您可以通过以下语法来调用方法：

```js
objectName.methodName()
```

通过 JavaScript，您能够定义并创建自己的对象。

创建新对象有两种不同的方法：

- 使用 Object 定义并创建对象的实例。
- 使用函数来定义对象，然后创建新的对象实例。

#### 使用 Object

在 JavaScript 中，几乎所有的对象都是 Object 类型的实例，它们都会从 Object.prototype 继承属性和方法。

Object 构造函数创建一个对象包装器。

Object 构造函数，会根据给定的参数创建对象，具体有以下情况：

- 如果给定值是 null 或 undefined，将会创建并返回一个空对象。
- 如果传进去的是一个基本类型的值，则会构造其包装类型的对象。
- 如果传进去的是引用类型的值，仍然会返回这个值，经他们复制的变量保有和源对象相同的引用地址。
- 当以非构造函数形式被调用时，Object 的行为等同于 new Object()。

语法格式：

```
// 以构造函数形式来调用
new Object([value])
```

value 可以是任何值。

以下实例使用 Object 生成布尔对象：

```js
// 等价于 o = new Boolean(true);
var o = new Object(true);
```







#### 也可以使用对象字面量来创建对象

```
var myObject = {
    key1: value1,
    key2: value2,
    // 更多键值对...
};
```

- **myObject:** 变量名，用于引用整个对象。
- **key1, key2:** 属性名称，可以是字符串或标识符。
- **value1, value2:** 属性的值，可以是任意 JavaScript 数据类型，包括数字、字符串、布尔值、函数、数组、甚至其他对象。

JavaScript 对象就是一个 **name:value** 集合。

#### 使用对象构造器

本例使用函数来构造对象：

```js
function person(firstname,lastname,age,eyecolor)
{
    this.firstname=firstname;
    this.lastname=lastname;
    this.age=age;
    this.eyecolor=eyecolor;
}

var myFather=new person("John","Doe",50,"blue");
var myMother=new person("Sally","Rally",48,"green");

```

在JavaScript中，this通常指向的是我们正在执行的函数本身，或者是指向该函数所属的对象（运行时）

#### 把属性添加到 JavaScript 对象

您可以通过为对象赋值，向已有对象添加新属性：

假设 person 对象已存在 - 您可以为其添加这些新属性：firstname、lastname、age 以及 eyecolor：

```js
person.firstname="John";
person.lastname="Doe";
person.age=30;
person.eyecolor="blue";

x=person.firstname;
```

#### 把方法添加到 JavaScript 对象

方法只不过是附加在对象上的函数。

在构造器函数内部定义对象的方法：

```
function person(firstname,lastname,age,eyecolor)
{
    this.firstname=firstname;
    this.lastname=lastname;
    this.age=age;
    this.eyecolor=eyecolor;

    this.changeName=changeName;
    function changeName(name)
    {
        this.lastname=name;
    }
}
```

changeName() 函数 name 的值赋给 person 的 lastname 属性。





#### JavaScript 类

JavaScript 是面向对象的语言，但 JavaScript 不使用类。

在 JavaScript 中，不会创建类，也不会通过类来创建对象（就像在其他面向对象的语言中那样）。

JavaScript 基于 prototype，而不是基于类的。



JavaScript for...in 循环

JavaScript for...in 语句循环遍历对象的属性。

### 语法

```
for (variable in object)
{
    执行的代码……
}
```

**注意：** for...in 循环中的代码块将针对每个属性执行一次。

## JavaScript 的对象是可变的

对象是可变的，它们是通过引用来传递的。

以下实例的 person 对象不会创建副本：

```
var x = person;  // 不会创建 person 的副本，是引用
```

如果修改 x ，person 的属性也会改变：

```js
var person = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"}
 
var x = person;
x.age = 10;           //  x.age 和 person.age 都会改变
```

**new 和不 new的区别：**

-  如果 new 了函数内的 this 会指向当前这个 person 并且就算函数内部不 return 也会返回一个对象。
-  如果不 new 的话函数内的 this 指向的是 window。

```js
function person(firstname,lastname,age,eyecolor)
{
    this.firstname=firstname;
    this.lastname=lastname;
    this.age=age;
    this.eyecolor=eyecolor;
    return [this.firstname,this.lastname,this.age,this.eyecolor,this] 
}

var myFather=new person("John","Doe",50,"blue");
var myMother=person("Sally","Rally",48,"green");
console.log(myFather) // this 输出一个 person 对象
console.log(myMother) // this 输出 window 对象
```



在 JavaScript 中，值类型（基本类型）本身不是对象，因此它们不直接拥有属性和方法。基本类型包括 Number、String、Boolean、Null、Undefined 和 Symbol, 这些类型的值都是简单的数据，而不是复杂的数据结构。

然而，JavaScript 为每种基本类型都提供了一个对应的包装对象。当你尝试访问一个基本类型的属性或方法时，JavaScript 会临时地将这个基本类型的值转换为对应的包装对象，以便可以调用该对象上的属性和方法。这种转换是自动进行的，通常被称为“装箱”。一旦属性和方法的使用完毕，这个临时对象就会被销毁，值又恢复为其原始的基本类型。

例如：

```js
let str = "Hello";
console.log(str.length); // 输出 5
```

在上面的代码中，str 是一个字符串基本类型的值。当我们尝试访问 str.length 时，JavaScript 会临时地将 str 转换为一个 String 对象，以便可以访问 length 属性。一旦 length 被获取，这个临时的 String 对象就会被销毁，str 仍然保持为字符串基本类型。



```js
let str = "hello";
console.log(str.toUpperCase());  // "HELLO"

// 实际上发生了什么：
// 1. JavaScript 临时创建一个 String 对象包装 "hello"
// 2. 在这个临时对象上调用 toUpperCase() 方法
// 3. 返回结果后，销毁这个临时对象

// 等价于：
let temp = new String("hello");
console.log(temp.toUpperCase());
// temp 被销毁
```



虽然这种装箱过程使得我们可以像操作对象一样操作基本类型，但基本类型本身并不是对象。它们只是简单的值，没有自己的内存空间来存储属性和方法。所有的方法和属性都是定义在对应的包装对象上的，而包装对象只在需要的时候被临时创建和使用。

需要注意的是，虽然我们可以这样使用基本类型，但频繁地进行装箱和拆箱操作（即将基本类型转换为对象，然后再转回基本类型）可能会影响性能，因此在性能敏感的代码中应尽量避免不必要的装箱操作。



| 基本类型  | 包装对象  | 示例                   |
| :-------- | :-------- | :--------------------- |
| `string`  | `String`  | `"abc".toUpperCase()`  |
| `number`  | `Number`  | `(123).toFixed(2)`     |
| `boolean` | `Boolean` | `true.toString()`      |
| `symbol`  | `Symbol`  | `Symbol().description` |
| `bigint`  | `BigInt`  | `123n.toString()`      |

注意：`null` 和 `undefined` **没有**包装对象，调用方法会报错。



### JavaScript prototype（原型对象）

在 JavaScript 中，原型（prototype）是一个非常重要的概念，它为对象提供了继承和共享属性的机制。每个 JavaScript 对象都有一个与之关联的原型对象，通过原型对象，可以实现属性和方法的共享，从而减少内存占用。

所有的 JavaScript 对象都会从一个 prototype（原型对象）中继承属性和方法。

- **原型**是一个**对象**，它是其他对象的模板或蓝图。
- 当一个对象试图访问一个属性或方法时，如果在该对象自身没有找到，JavaScript 会沿着原型链向上查找，直到找到对应的属性或方法，或者达到原型链的顶端 `null` 为止。

------

### 对象的 \__proto__ 属性

每个 JavaScript 对象（除了 null）都自动拥有一个隐藏的属性 __proto__，它指向该对象的原型对象。这个 __proto__ 是实现继承的关键：

```js
let obj = {};
console.log(obj.__proto__); // 输出: [object Object], 即 obj 的原型是 Object.prototype
```



我们也知道在一个已存在构造器的对象中是不能添加新的属性：

Person.nationality = "English";

要添加一个新的属性需要在在构造器函数中添加：

```js
function Person(first, last, age, eyecolor) {
  this.firstName = first;
  this.lastName = last;
  this.age = age;
  this.eyeColor = eyecolor;
  this.nationality = "English";
}
```





当你使用构造函数创建一个对象时，构造函数的 **prototype** 属性会成为所有该构造函数创建的实例对象的原型。



```js
**function** Person(name) {
  **this**.name = name;
}

Person.**prototype**.sayHello = **function**() {
  console.log("Hello, my name is " + **this**.name);
};

let alice = **new** Person("Alice");
alice.sayHello(); *// 输出: Hello, my name is Alice*
```



在这个例子中，Person.prototype 是 alice 对象的原型，因此 alice 可以访问 sayHello 方法。





#### 原型链

在 JavaScript 中，对象通过原型链（prototype chain）来实现继承。当一个对象尝试访问一个属性或方法时，JavaScript 会首先检查该对象自身是否有这个属性或方法。如果没有，它会沿着原型链向上查找。

```js
let obj = {};
console.log(obj.toString()); // 输出: [object Object]
// 这个 `toString` 方法实际上是从 `Object.prototype` 继承过来的
```

在上面的例子中，obj 对象没有定义 toString 方法，因此 JavaScript 沿着原型链查找，最终在 Object.prototype 中找到该方法。

你可以动态地修改对象的原型，这样可以影响到所有基于该原型创建的对象：

实例

```js
**function** Person(name) {
  **this**.name = name;
}

Person.**prototype**.sayHello = **function**() {
  console.log("Hello, my name is " + **this**.name);
};

let bob = **new** Person("Bob");
bob.sayHello(); *// 输出: Hello, my name is Bob*

*// 修改原型*
Person.**prototype**.sayGoodbye = **function**() {
  console.log("Goodbye from " + **this**.name);
};

bob.sayGoodbye(); *// 输出: Goodbye from Bob*
```







```js

// JavaScript 的原型链
let animal = {
    eat() {
        console.log("Eating...");
    }
};

let dog = {
    bark() {
        console.log("Barking...");
    }
};

// 设置原型链：dog 的原型指向 animal
Object.setPrototypeOf(dog, animal);

dog.bark();  // 自己的方法
dog.eat();   // 沿着原型链找到 animal 的方法
```

// 类比：复印机 vs 链接

// 类继承（Java）—— 复印机模式
// 父类 = 原件，子类 = 复印件
// 修改原件 → 已经复印出来的复印件不会改变

// 原型链（JavaScript）—— 超链接模式
// 父对象 = 源网页，子对象 = 快捷方式/链接
// 修改源网页 → 所有快捷方式访问到的都是新内容

**类继承是“复制”模型——子类获得父类的副本；原型链是“委托”模型——子对象通过链接查找父对象的方法。**

| 特性         | 类继承（Java/Python）                    | 原型链（JavaScript）             |
| :----------- | :--------------------------------------- | :------------------------------- |
| **机制**     | 复制属性和方法                           | 委托（delegation）               |
| **关系**     | 类是模板，对象是实例                     | 对象链接到另一个对象             |
| **继承方式** | 静态（编译时确定）                       | 动态（运行时查找）               |
| **修改父类** | 不影响已创建的实例                       | 会影响所有子对象                 |
| **本质**     | 复制一份                                 | 引用链式查找                     |
| **方法共享** | 每个实例复制一份方法（或共享类的方法表） | 所有实例通过原型链共享同一个方法 |
| **内存结构** | 实例包含所有属性和方法（或指针）         | 实例只有自身属性，方法在原型链上 |
| **动态性**   | 静态（编译时确定）                       | 动态（运行时查找）               |



#### 原型链的本质：委托

```js
// 演示委托机制
let parent = {
    name: "Parent",
    greet() {
        console.log(`Hello, I'm ${this.name}`);
    }
};

let child = {
    name: "Child"
};

Object.setPrototypeOf(child, parent);

child.greet();  // "Hello, I'm Child" - 注意 this 指向 child

// 当调用 child.greet() 时：
// 1. child 自身有 greet 方法吗？没有
// 2. 沿着原型链找到 parent.greet
// 3. 调用 parent.greet，但 this 绑定到 child
// 4. 所以输出的是 child 的 name

这不是"复制"，而是"委托"——对象自己没有的方法，委托给原型链上的其他对象执行。
```

这不是"复制"，而是"委托"——对象自己没有的方法，委托给原型链上的其他对象执行。

与java、python等继承的区别？

```java
// Java 中，修改父类不会影响已创建的实例
class Animal {
    void eat() { }
}

Dog dog = new Dog();
dog.eat();  // 可以调用

// 如果给 Animal 添加新方法，已创建的 dog 对象不会获得这个方法
// 需要重新编译整个程序
JAVA
┌─────────────────────────────────────────────────┐
│                   编译时                         │
├─────────────────────────────────────────────────┤
│  Animal 类 (存储在方法区/代码段)                  │
│  ├── sound: "Some sound"                        │
│  └── makeSound() 方法代码                        │
└─────────────────────────────────────────────────┘
                    ↓ 编译时复制/生成
┌──────────────────────────────┐  ┌──────────────────────────────┐
│     Dog 类定义 (方法区)       │  │     Dog 类定义 (方法区)       │
│  ├── 复制了 Animal 的所有成员  │  │  ├── 复制了 Animal 的所有成员  │
│  └── + bark() 方法            │  │  └── + bark() 方法            │
└──────────────────────────────┘  └──────────────────────────────┘
                    ↓ new 实例                    ↓ new 实例
┌──────────────────────────────┐  ┌──────────────────────────────┐
│     dog1 实例 (堆内存)        │  │     dog2 实例 (堆内存)        │
│  ├── sound: "Some sound"     │  │  ├── sound: "Some sound"     │
│  └── 方法表指针 → Dog 类      │  │  └── 方法表指针 → Dog 类      │
└──────────────────────────────┘  └──────────────────────────────┘
         ↑ 指向相同类定义              ↑ 指向相同类定义
         │                            │
         └────────────┬───────────────┘
                      ↓
              Dog 类定义（共享）
    
    
JS
    ┌─────────────────────────────────────────────────┐
│                运行时（动态）                     │
├─────────────────────────────────────────────────┤
│  Animal.prototype (原型对象)                     │
│  ├── sound: "Some sound"                        │
│  └── makeSound: [Function]                      │
└─────────────────────────────────────────────────┘
         ↑ 原型链链接          ↑ 原型链链接
         │                    │
┌──────────────────────────────┐  ┌──────────────────────────────┐
│     dog1 实例 (堆内存)        │  │     dog2 实例 (堆内存)        │
│  ├── (无自身属性)             │  │  ├── (无自身属性)             │
│  └── __proto__ ──────────────┘  └── __proto__ ──────────────┘
│      指向 Animal.prototype           指向 Animal.prototype
└──────────────────────────────┘  └──────────────────────────────┘

// 注意：实例没有方法副本，只有链接！
// 所有实例共享同一个原型对象
```





ES6 的 `class` 语法让原型链看起来更像传统继承：

javascript

```js
// ES6 class 语法
class Animal {
    eat() {
        console.log("Eating...");
    }
}

class Dog extends Animal {  // 看起来像类继承
    bark() {
        console.log("Barking...");
    }
}

let dog = new Dog();
dog.eat();   // 看起来像继承了父类
dog.bark();

// 但底层仍然是原型链
console.log(Dog.prototype.__proto__ === Animal.prototype);  // true
```



**本质**：ES6 class 是原型链的语法糖，不是真正的类继承。

- 你可以把原型链**类比为**父类继承（都能实现方法复用）
- 但要记住底层原理完全不同：
  - 类继承 = 复制一份（独立）
  - 原型链 = 引用链接（共享）



new操作符

```js
function Person(name) {
    this.name = name;
}
// 1. 创建 Person.prototype 对象
// 2. 设置 Person.prototype.constructor = Person

let p = new Person("Alice");

// new 操作符内部做了这几件事：
// 1. 创建一个空对象
// let obj = {};

// 2. 将这个空对象的 __proto__ 指向 Person.prototype
// obj.__proto__ = Person.prototype;

// 3. 将 this 指向这个新对象，并执行构造函数
// Person.call(obj, "Alice");

// 4. 返回这个对象
// return obj;
```



Object.create 方法允许你创建一个新对象，并将其原型设置为指定的对象。

```js
let proto = { greet: "Hello" ,

wave :function(){},

};
let obj = Object.create(proto);
// obj 的原型直接是 proto
// proto 是普通对象，没有 prototype 属性
console.log(obj.__proto__ === proto);  // true
console.log(obj.__proto__===proto.prototype)//false
console.log(typeof(proto));//object
console.log(typeof(proto.__proto__));//object，proto.__proto__ 指向 Object.prototype
console.log(typeof(proto.prototype));//undefined
console.log(typeof(obj));//object
console.log(proto.prototype);//undefined
console.log(proto.__proto__);//[Object: null prototype] {}
console.log(proto.prototype.constructor);//TypeError: Cannot read properties of undefined (reading 'constructor')
console.log(proto.constructor);//[Function: Object]，constructor 来自 Object.prototype。
console.log(proto.__proto__.constructor);//[Function: Object]

// 内存结构图
proto (普通对象)
├── greet: "Hello"
├── wave: [Function]
└── __proto__ ──→ Object.prototype
                  ├── constructor: Object ←─┐
                  ├── toString: [Function]  │
                  ├── hasOwnProperty: ...   │
                  └── __proto__: null       │
                                            │
obj (通过 Object.create(proto) 创建)         │
├── (无自身属性)                             │
└── __proto__ ──→ proto (直接指向)           │
                                            │
// constructor 的查找链                      │
proto.constructor ──────────────────────────┘
proto.__proto__.constructor ────────────────┘
// 两者都指向 Object

function Person() {}

Person.prototype.greet = "Hello";
Person.prototype.ydc=function(){};
Person.hello="asd";
Person.stc=function (){};

let obj2 = new Person();
// obj 的原型是 Person.prototype
console.log(typeof(Person));//function
console.log(typeof(Person.prototype));//object
console.log(typeof(Person.__proto__));//function
console.log(obj2.__proto__===Person);//false
console.log(obj2.__proto__===Person.prototype);  // true
console.log(Person.__proto__===Person.prototype);//false
console.log(Person.prototype.constructor === Person)//true Person.prototype.constructor 指向的就是 Person 函数本身！
console.log(Person.prototype===Person)//false
console.log(Person.prototype)//{ greet: 'Hello', ydc: [Function (anonymous)] }
console.log(Person.__proto__)//[Function (anonymous)] Object
console.log(Person.prototype.constructor)//[Function: Person] { hello: 'asd', stc: [Function (anonymous)] }
console.log(Person.constructor)//[Function: Function]
console.log(Person.__proto__.constructor);//[Function: Function]
console.log(Person.prototype.__proto__);//[Object: null prototype] {}
console.log(obj2.__proto__)//{ greet: 'Hello', ydc: [Function (anonymous)] }
console.log(obj2.prototype)//undefined

console.log(Person.__proto__.prototype);//undefined
console.log(Person.__proto__.__proto__);//[Object: null prototype] {}


console.log(obj2.__proto__.prototype);//undefined
console.log(obj2.__proto__.__proto__);//[Object: null prototype] {}
function Person(name,age) {
    this.name = name;
    this.age=age;
}

// 给构造函数添加属性
Person.nationality = "English";

Person.isAdult = function  (age) {
    return age >= 18;
};
let p = new Person("Alice",18);

// 构造函数本身有这个属性
console.log(Person.nationality);  // "English" ✅
console.log(Person.isAdult);//[Function (anonymous)]
console.log(Person.isAdult(14))
// 实例没有这个属性
console.log(p.nationality);  // undefined ❌
console.log(p.isAdult);//undifined

console.log(p.__proto__ === Person.prototype);  // true

```







```js

function Person() {}
Person.prototype.greet = "Hello";
Person.hello="asdfds";
Person.prototype.std="ads"
let obj2 = new Person();
// obj 的原型是 Person.prototype
console.log(obj2.__proto__===Person);//false
console.log(obj2.__proto__===Person.prototype);  // true
console.log(Person.__proto__===Person.prototype);//false
console.log(Person.prototype.constructor === Person)//true
console.log(Person.prototype===Person)//false
console.log(Person.prototype)//{ greet: 'Hello', std:"ads"}
console.log(Person.__proto__)//[Function (anonymous)] Object或者[Function] (Function.prototype)
console.log(Person.prototype.constructor)////[Function: Person] { hello: 'asdfds', }


┌─────────────────────────────────────────────────────────┐
│                       全局作用域                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  Person (构造函数)                                       │
│  ├── 类型: Function                                     │
│  ├── 自身属性: hello: "asdfds"                          │
│  ├── prototype ──────────────┐                          │
│  └── __proto__ ──→ Function.prototype                  │
│                              │                          │
│                              ↓                          │
│                    Person.prototype (原型对象)           │
│                    ├── 类型: Object                     │
│                    ├── 自身属性: greet: "Hello"         │
│                    ├── 自身属性: std: "ads"             │
│                    ├── constructor ──→ Person           │
│                    └── __proto__ ──→ Object.prototype   │
│                                                         │
│                    obj2 (实例对象)                       │
│                    ├── 类型: Person 实例                 │
│                    ├── 自身属性: (无，除非在构造函数内赋值) │
│                    └── __proto__ ──→ Person.prototype   │
│                                                         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────┐
│                                                 │
│  Function (内置构造函数)                         │
│  ├── prototype ──→ Function.prototype          │
│  └── __proto__ ──→ Function.prototype          │
│                                                 │
│  Function.prototype (对象)                       │
│  ├── 类型: "function"                           │
│  ├── 名称: "" (匿名)                            │
│  ├── call: [Function: call]                    │
│  ├── apply: [Function: apply]                  │
│  ├── bind: [Function: bind]                    │
│  └── __proto__ ──→ Object.prototype            │
│           ↑                                     │
│           │ 指向                                 │
│  Person (你定义的函数)                           │
│  ├── prototype ──→ Person.prototype            │
│  └── __proto__ ──┘                              │
│                                                 │
└─────────────────────────────────────────────────┘
```

1. **`prototype` - 构造函数的属性**

- 属于**构造函数**（Function对象）
- 指向一个对象(object)（原型对象）
- **只在函数上存在**

2. **`__proto__` - 实例对象的属性**

- 属于**实例对象**
- 指向构造函数(Funciton)的 `prototype`
- **所有对象都有**（除了 `Object.create(null)`）

3. **原型对象 - 自动创建的对象**

当创建一个函数时，JS引擎会**自动创建**这个函数的原型对象，并建立双向引用。



```js
// 你写的代码：
function Person() {}

// JS 引擎实际帮你做了（伪代码）：
const Person = function() {};

// 1. 自动创建 Person.prototype 对象
Person.prototype = {
    constructor: Person,  // 指向 Person 本身
    __proto__: Object.prototype  // 原型链指向 Object
};

// 2. Person 对象的 __proto__ 指向 Function.prototype
Person.__proto__ = Function.prototype;
// 3. 建立它们之间的双向连接
```





| 属性        | 属于谁   | 作用                                                         |
| :---------- | :------- | :----------------------------------------------------------- |
| `prototype` | 构造函数 | 指向原型对象，用于定义共享方法                               |
| `__proto__` | 实例对象 | 指向构造函数的 `prototype`，用于原型链查找  **`prototype` - 构造函数的属性** |

`Person` - 构造函数

- **类型**：Function（函数对象）
- **作用**：用来创建实例的模板
- **自身属性**：`hello`（你添加的）、`prototype`（函数自带）
- **`__proto__`**：指向 `Function.prototype`

javascript

```
console.log(typeof Person);  // "function"
console.log(Person.hello);   // "asdfds" - 这是Person自身的属性
console.log(Person.prototype); // 指向原型对象
```



 `Person.prototype` - 原型对象

- **类型**：Object（普通对象）
- **作用：存放所有实例共享的属性和方法**
- **自身属性**：`greet`、`std`、`constructor`
- **`__proto__`**：指向 `Object.prototype`

javascript

```js
console.log(typeof Person.prototype);  // "object"
console.log(Person.prototype.greet);   // "Hello" - 实例共享
console.log(Person.prototype.constructor); // Person函数本身
```

`Person.__proto__` - 构造函数的原型链指向

- **类型**：指向 `Function.prototype`
- **作用**：让 `Person` 能访问 `Function.prototype` 上的方法（如 `call`、`apply`、`bind`）

javascript

```js
console.log(Person.__proto__ === Function.prototype);  // true
console.log(Person.__proto__); // [Function] (Function.prototype)
```

`__proto__：指向 Function.prototype ,这里为什么__proto__：指向 Function.prototype ？`

所有函数都是 Function 的实例

在 JavaScript 中，**函数本身就是对象**，它们是由内置构造函数 `Function` 创建的实例。

javascript

```
// 你定义的函数
function Person() {}

// 等价于
const Person = new Function('...');

// 所以 Person 是 Function 的实例
console.log(Person instanceof Function);  // true
console.log(Person.__proto__ === Function.prototype);  // true
```



如何查看静态属性？

方法1：直接打印构造函数

javascript

```
function Person() {}
Person.hello = "asdfds";
Person.staticMethod = function() {};

console.log(Person);
// 输出：[Function: Person] { hello: 'asdfds', staticMethod: [Function (anonymous)] }
```



方法2：使用 `Object.keys()` 或 `Object.getOwnPropertyNames()`

javascript

```
function Person() {}
Person.hello = "asdfds";
Person.staticMethod = function() {};
Person.prototype.greet = "Hello";

// 查看静态属性（构造函数自身的属性）
console.log(Object.keys(Person));  
// ['hello', 'staticMethod'] 或可能包含其他

console.log(Object.getOwnPropertyNames(Person));
// 返回：['length', 'name', 'prototype', 'hello', 'staticMethod', ...]
```

- 构造函数直接点（`Person.xxx`）→ 访问自己的静态属性
- 构造函数点 prototype 再点（`Person.prototype.xxx`）→ 访问原型属性
- 实例直接点（`instance.xxx`）→ 自动查找原型链



```js
let proto = { greet: "Hello" ,

wave :function(){},

};
let obj = Object.create(proto);
// obj 的原型直接是 proto
// proto 是普通对象，没有 prototype 属性
console.log(obj.__proto__ === proto);  // true
console.log(obj.__proto__===proto.prototype)//false
console.log(typeof(proto));//object
console.log(typeof(proto.__proto__));//object，proto.__proto__ 指向 Object.prototype
console.log(typeof(proto.prototype));//undefined
console.log(typeof(obj));//object
console.log(proto.prototype);//undefined
console.log(proto.__proto__);//[Object: null prototype] {}
console.log(proto.prototype.constructor);//TypeError: Cannot read properties of undefined (reading 'constructor')
console.log(proto.constructor);//[Function: Object]，constructor 来自 Object.prototype。
console.log(proto.__proto__.constructor);//[Function: Object]

// 内存结构图
proto (普通对象)
├── greet: "Hello"
├── wave: [Function]
└── __proto__ ──→ Object.prototype
                  ├── constructor: Object ←─┐
                  ├── toString: [Function]  │
                  ├── hasOwnProperty: ...   │
                  └── __proto__: null       │
                                            │
obj (通过 Object.create(proto) 创建)         │
├── (无自身属性)                             │
└── __proto__ ──→ proto (直接指向)           │
                                            │
// constructor 的查找链                      │
proto.constructor ──────────────────────────┘
proto.__proto__.constructor ────────────────┘
// 两者都指向 Object
```



constructor 的来源

javascript

```
// 普通对象：constructor 来自原型链
let obj = {};
console.log(obj.constructor);  // Object - 来自 Object.prototype

// 函数对象：有自己专属的 constructor
function Person() {}
console.log(Person.constructor);  // Function - 来自 Function.prototype
console.log(Person.prototype.constructor);  // Person - 指向自己
```



3. 两种创建对象的方式

javascript

```
// 方式1：构造函数
function Person() {}
Person.prototype.greet = "Hello";
const p = new Person();
// 原型链：p → Person.prototype → Object.prototype

// 方式2：Object.create()
let proto = { greet: "Hello" };
const obj = Object.create(proto);
// 原型链：obj → proto → Object.prototype
```



完整对比表

| 特性                           | 构造函数方式                                   | Object.create() 方式                 |
| :----------------------------- | :--------------------------------------------- | :----------------------------------- |
| **原型对象**                   | `Person.prototype`（自动创建，有 constructor） | 自定义对象（如 `personPrototype`）   |
| **创建实例**                   | `new Person()`                                 | `Object.create(proto)`               |
| **原型对象是否有 prototype**   | ✅ 有（函数才有）                               | ❌ 无（普通对象）                     |
| **原型对象是否有 constructor** | ✅ 有，指向构造函数                             | ❌ 无（除非手动添加）                 |
| **原型链**                     | 实例 → Person.prototype → Object.prototype     | 实例 → 自定义原型 → Object.prototype |

prototype继承



所有的 JavaScript 对象都会从一个 prototype（原型对象）中继承属性和方法：

- `Date` 对象从 `Date.prototype` 继承。
- `Array` 对象从 `Array.prototype` 继承。
- `Person` 对象从 `Person.prototype` 继承。

所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。

JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时，它不仅仅在该对象上搜寻，还会搜寻该对象的原型，以及该对象的原型的原型，依次层层向上搜索，直到找到一个名字匹配的属性或到达原型链的末尾。

`Date` 对象, `Array` 对象, 以及 `Person` 对象从 `Object.prototype` 继承。

**给已有的函数对象增加属性或者方法**

格式：**构造函数名.prototype.新属性或者新方法**



**字面量方法**指的是使用对象字面量 `{}` 直接给 `prototype` 赋值的方式。

```js
注意，千万不要使用字面量方式来定义属性和方法，否则原有属性和方法会被重写：

function Fn() {};

// 定义属性
Fn.prototype.a = 1;
Fn.prototype.b = 2;

// 字面量定义方法，原型被重写，原有属性和方法被更新
Fn.prototype = {
  c : function() {
    return this.a + this.b;
  }
}
```





```js
class Runoob {
  constructor(name, year) {
    this.name = name;
    this.year = year;
  }
  age() {
    return new Date().getFullYear() - this.year;
  }
}

console.log(typeof Runoob); // "function" - class 本质上是一个函数！

等价于不用class
// 这是 class 背后的真实代码
function Runoob(name, year) {
  // constructor 的内容
  this.name = name;
  this.year = year;
}

// age 方法被添加到 prototype 上
Runoob.prototype.age = function() {
  let date = new Date();
  return date.getFullYear() - this.year;
};

// 使用方式完全相同
let runoob = new Runoob("菜鸟教程", 2018);
console.log(runoob.age()); // 输出相同结果
```

**class 的优势**：提供了更清晰、更面向对象的语法，并添加了必要的安全检查，让代码更易于理解和维护。它是对传统构造函数+原型模式的现代化封装。



Object.create() 与 new 的本质区别

一、核心差异一句话

> **`new Person()` 会执行构造函数进行初始化，`Object.create()` 只建立原型链，不执行任何初始化逻辑。**

```js

// 定义一个构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.sayHi = function() {
    console.log(`Hi, I'm ${this.name}`);
  };
}

Person.prototype.greet = function() {
  console.log(`Hello, I'm ${this.name}`);
};

// 方式1：new
let p1 = new Person("Alice", 25);

// 方式2：Object.create
let proto = Person.prototype;
let p2 = Object.create(proto);
// 注意：p2 此时 name 和 age 都是 undefined，因为构造函数没执行！
new操作符：
function myNew(Constructor, ...args) {
  // 步骤1：创建空对象，原型指向 Constructor.prototype
  let obj = Object.create(Constructor.prototype);
  
  // 步骤2：执行构造函数，绑定 this 到新对象
  let result = Constructor.apply(obj, args);
  
  // 步骤3：如果构造函数返回对象，则返回该对象；否则返回步骤1创建的对象
  return result instanceof Object ? result : obj;
}

// 等价于
let p1 = myNew(Person, "Alice", 25);
Object.create()操作符：
// Object.create 的简化实现
function myObjectCreate(proto) {
  function F() {}
  F.prototype = proto;
  return new F();
}

// 等价于
let p2 = myObjectCreate(Person.prototype);
new Person() 
p1 (实例对象)
├── name: "Alice"          ← 构造函数添加的
├── age: 25                ← 构造函数添加的
├── sayHi: [Function]      ← 构造函数添加的
└── __proto__ ──→ Person.prototype
                  ├── greet: [Function]
                  ├── constructor: Person
                  └── __proto__ ──→ Object.prototype
Object.create(Person.prototype) 

p2 (实例对象)
├── (空对象，没有 name、age、sayHi)
└── __proto__ ──→ Person.prototype
                  ├── greet: [Function]
                  ├── constructor: Person
                  └── __proto__ ──→ Object.prototype
```



| 维度               | `new Person()`               | `Object.create(proto)`       |
| :----------------- | :--------------------------- | :--------------------------- |
| **执行构造函数**   | ✅ 执行                       | ❌ 不执行                     |
| **初始化属性**     | ✅ 会赋值                     | ❌ 属性为 undefined           |
| **原型来源**       | 自动指向 `Person.prototype`  | 手动指定任何对象             |
| **this 绑定**      | 绑定到新对象                 | 无函数执行，无 this          |
| **适用场景**       | 需要初始化的对象             | 只需要原型链的对象           |
| **是否可模拟**     | 可用 Object.create 模拟      | 无法模拟 new 的初始化        |
| **创建的对象类型** | `instanceof Person === true` | `instanceof Person === true` |

`new` = 创建对象 + 执行初始化
`Object.create` = 创建对象 + 指定原型（不初始化）



> **C# 的“委托（delegate）”和 JavaScript 原型链的“委托（delegation）”是完全不同的概念，只是中文翻译恰好相同。**

| 维度             | C# 委托 (delegate)           | JavaScript 原型委托 (prototype delegation) |
| :--------------- | :--------------------------- | :----------------------------------------- |
| **英文原词**     | Delegate                     | Delegation                                 |
| **本质**         | 类型安全的函数指针           | 对象间的方法查找机制                       |
| **作用**         | 将方法作为参数传递、事件处理 | 实现继承、共享方法                         |
| **层级**         | 语法特性                     | 语言核心机制                               |
| **与继承的关系** | 无关，是回调机制             | 就是继承的实现方式                         |



#### C# 委托（Delegate）是什么

C# 的委托是**类型安全的函数指针**，用于将方法作为参数传递。

```C#
// 1. 声明委托类型
public delegate void PrintHandler(string message);

// 2. 定义匹配的方法
public static void PrintToConsole(string msg) 
{
    Console.WriteLine(msg);
}

public static void PrintToFile(string msg) 
{
    File.WriteAllText("log.txt", msg);
}

// 3. 使用委托
PrintHandler handler = PrintToConsole;
handler("Hello");  // 输出到控制台

handler = PrintToFile;
handler("Hello");  // 写入文件

// 4. 多播委托（调用多个方法）
handler += PrintToConsole;
handler("Hello");  // 既写文件又输出控制台
```



**核心特征**：

- 类型安全（编译时检查签名）
- 可以指向实例方法和静态方法
- 支持多播（调用链）
- 是事件驱动编程的基础

### JS严格模式（use strict）

"use strict" 的目的是指定代码在严格条件下执行。

严格模式下你不能使用未声明的变量。

```js
"use strict";
myFunction();

function myFunction() {
    y = 3.14;   // 报错 (y 未定义)
}
在函数内部声明是局部作用域 (只在函数内使用严格模式):
x = 3.14;       // 不报错
myFunction();

function myFunction() {
   "use strict";
    y = 3.14;   // 报错 (y 未定义)
}
```

#### 一、变量相关

1. **变量必须声明**
   `x = 3.14;` ❌ 未声明的变量不允许使用。
2. **不能删除变量或函数**
   `delete x;` ❌ 删除变量、函数都会报错。
3. **变量名不能是 `eval` 或 `arguments`**
   `var eval = 3.14;` ❌
   `var arguments = 3.14;` ❌

------

#### 二、函数相关

1. **函数参数不能重名**
   `function f(p1, p1) {}` ❌

2. **函数内部 `this` 不指向全局**

   - 非严格模式：`this` 指向全局对象
   - 严格模式：`this` 为 `undefined`，直接调用函数时 `this` 未定义会报错

   ```
   禁止this关键字指向全局对象。
   
   function f(){
       return !this;
   } 
   // 返回false，因为"this"指向全局对象，"!this"就是false
   
   function f(){ 
       "use strict";
       return !this;
   } 
   // 返回true，因为严格模式下，this的值为undefined，所以"!this"为true。
   因此，使用构造函数时，如果忘了加new，this不再指向全局对象，而是报错。
   
   function f(){
       "use strict";
       this.a = 1;
   };
   f();// 报错，this未定义
   ```

   

------

#### 三、对象与属性相关

1. **不能对只读属性赋值**

   ```
Object.defineProperty(obj, "x", {value: 0, writable: false});
   obj.x = 3.14;  // ❌
   ```
   
   

2. **不能对仅 getter 的属性赋值**

   ```
var obj = {get x() {return 0;}};
   obj.x = 3.14;  // ❌
   ```
   
   

3. **不能删除不可删除的属性**
   `delete Object.prototype;` ❌

------

#### 四、语法与关键字

1. **禁止八进制表示**
   `var x = 010;` ❌

2. **禁止转义字符**（类似 `\010`）
   `var x = \010;` ❌

3. **禁止 `with` 语句**
   `with (Math) {x = cos(2);}` ❌

4. **新增保留字**
   以下不能作为变量名、函数名等：

   ```js
   implements, interface, let, package,
   private, protected, public, static, yield
   ```

   

   `var public = 1500;` ❌



#### 其它

`eval` 中声明的变量不能在外部访问

```js
eval("var x = 2;");
alert(x);  // ❌
```





## JS错误一览

1. 这种错误经常会在 switch 语句中出现，switch 语句会使用恒等计算符(===)进行比较:

2. JavaScript 中的所有数据都是以 64 位**浮点型数据(float)** 来存储。

   所有的编程语言，包括 JavaScript，对浮点型数据的精确度都很难确定：

   为解决以上问题，可以用整数的乘除法来解决：

   `var z = (x * 10 + y * 10) / 10;    // z 的结果为 0.3`

3. 在字符串中直接使用回车换行是会报错的：

   ```js
   var x = "Hello
   World!";
   
   var x = "Hello\
   World!";
   ```

4. 错误地使用分号

   以下实例中，if 语句失去方法体，原 if 语句的方法体作为独立的代码块被执行，导致错误的输出结果。由于分号使用错误，if 语句中的代码块就一定会执行：

   ```js
   if (x == 19);{
   	    // code block  
   }
   ```

   return 语句使用注意事项

   JavaScript 默认是在一行的末尾自动结束。

   JavaScript 也可以使用多行来表示一个语句，也就是说一个语句是可以分行的。

   但是，以下实例结果会返回 **undefined**：

   ```js
   function myFunction(a) {
     var
     power = 10; 
     return
     a * power;
   }
   ```

   为什么会有这样的结果呢？因为在 JavaScript 中，实例 4 的代码与下面的代码一致：

   ```
   function myFunction(a) {
       var
       power = 10;  
       return;       // 分号结束，返回 undefined
       a * power;
   }
   ```

   如果是一个不完整的语句，如下所示:

   ```
   var
   ```

   JavaScript 将尝试读取第二行的语句：

   ```
   power = 10;
   ```

   但是由于这样的语句是完整的:

   ```
   return
   ```

   JavaScript 将自动关闭语句:

   ```
   return;
   ```

   在 JavaScript 中，分号是可选的 。

   由于 return 是一个完整的语句，所以 JavaScript 将关闭 return 语句。

​	不用对 return 语句进行断行。

```js
return 
ture;
//JavaScript会解析成：
return ;  true;
//而代码本意是这样的：
return   true;
```

5. 数组中使用名字来索引

   许多程序语言都允许使用名字来作为数组的索引。

   使用名字来作为索引的数组称为关联数组(或哈希)。

   JavaScript 不支持使用名字来索引数组，只允许使用数字索引。

   在 JavaScript 中, **对象** 使用 **名字作为索引**。

   如果你使用名字作为索引，当访问数组时，JavaScript 会把数组重新定义为标准对象。

   执行这样操作后，数组的方法及属性将不能再使用，否则会产生错误:

   ```js
   var person = [];
   person["firstName"] = "John";
   person["lastName"] = "Doe";
   person["age"] = 46;var x = person.length;         // person.length 返回 0
   var y = person[0];              // person[0] 返回 undefined
   ```



