JavaScript .prototype 是如何工作的?

问:

我不是那么喜欢动态编程语言,但我已经编写了相当一部分的 JavaScript 代码。我从来没有真正理解过这种基于原型的编程,有人知道它是如何工作的吗?

var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

我记得前段时间与人们进行了很多讨论(我不确定自己在做什么),但据我所知,没有类的概念。它只是一个对象,这些对象的实例是原始对象的克隆,对吗?

但是 JavaScript 中这个“.prototype”属性的确切用途是什么?它与实例化对象有什么关系?

更新:正确的方法

var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!

function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK

这些slides也确实有很大帮助。

答1:

huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效。

在 Java、C# 或 C++ 等实现经典继承的语言中,您首先创建一个类(对象的蓝图),然后您可以从该类创建新对象,或者您可以扩展该类,定义一个新类来增强原来的班级。

在 JavaScript 中,您首先创建一个对象(没有类的概念),然后您可以扩充自己的对象或从中创建新对象。对于习惯了经典方式的人来说,这并不难,但有点陌生且难以代谢。

例子:

//在 JavaScript 中定义一个函数对象来保存人员 var Person = function(name) { this.name = name; }; //向已经定义的对象动态添加一个新的getter Person.prototype.getName = function() { return this.name; }; //创建一个 Person 类型的新对象 var john = new Person(“John”); //尝试getter alert(john.getName()); //如果现在我修改了人,约翰也会得到更新 Person.prototype.sayMyName = function() { alert('Hello, my name is ’ + this.getName()); }; //调用john john.sayMyName()的新方法;

到目前为止,我一直在扩展基础对象,现在我创建另一个对象,然后从 Person 继承。

//Create a new object of type Customer by defining its constructor. It's not 
//related to Person for now.
var Customer = function(name) {
    this.name = name;
};

//Now I link the objects and to do so, we link the prototype of Customer to 
//a new instance of Person. The prototype is the base that will be used to 
//construct all new instances and also, will modify dynamically all already 
//constructed objects because in JavaScript objects retain a pointer to the 
//prototype
Customer.prototype = new Person();     

//Now I can call the methods of Person on the Customer, let's try, first 
//I need to create a Customer.
var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();

//If I add new methods to Person, they will be added to Customer, but if I
//add new methods to Customer they won't be added to Person. Example:
Customer.prototype.setAmountDue = function(amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function() {
    return this.amountDue;
};

//Let's try:       
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());

var Person = function (name) { this.name = name; }; Person.prototype.getName = function () { return this.name; }; var john = new Person(“John”);警报(约翰.getName()); Person.prototype.sayMyName = function () { alert(‘你好,我叫’ + this.getName()); };约翰.sayMyName(); var Customer = function (name) { this.name = name; }; Customer.prototype = new Person(); var myCustomer = new Customer(‘Dream Inc.’); myCustomer.sayMyName(); Customer.prototype.setAmountDue = function (amountDue) { this.amountDue = amountDue; }; Customer.prototype.getAmountDue = function () { return this.amountDue; }; myCustomer.setAmountDue(2000);警报(myCustomer.getAmountDue());

虽然如前所述,我不能在 Person 上调用 setAmountDue()、getAmountDue()。

//The following statement generates an error.
john.setAmountDue(1000);

我认为 stackoverflow 上的答案不仅对原始发布者感兴趣,而且对潜伏或来自搜索的其他人的大社区也很有趣。我一直是其中之一,我从旧职位中受益。我想我可以为其他答案添加一些代码示例。关于你的问题:如果你遗漏了新的,它就行不通。当我调用 myCustomer.sayMyName() 时,它返回“myCustomer.sayMyName 不是函数”。最简单的方法是试验萤火虫,看看会发生什么。

据我了解 var Person = function (name) {...};正在定义一个能够构建 Person 对象的构造函数。所以还没有Object,只有匿名构造函数被赋值给Person。这是一个很好的解释:helephant.com/2008/08/how-javascript-objects-work

警告:这个答案忽略了父类构造函数不是基于每个实例调用的事实。它起作用的唯一原因是因为他在子构造函数和父构造函数中做了完全相同的事情(设置名称)。有关尝试在 JavaScript 中进行继承时所犯的常见错误的更深入解释(以及最终解决方案),请参阅:this stack overflow post

关于 Customer.prototype = new Person(); 行,MDN 展示了一个使用 Customer.prototype = Object.create(Person.prototype) 的示例,并指出 '此处的常见错误是使用 "new Person()"'。 source

答2:

huntsbot.com提供全网独家一站式外包任务、远程工作、创意产品分享与订阅服务!

每个 JavaScript 对象 has an internal “slot” 称为 [[Prototype]],其值为 null 或 object。您可以将插槽视为对象的属性,位于 JavaScript 引擎内部,对您编写的代码隐藏。 [[Prototype]] 周围的方括号是经过深思熟虑的,是表示内部槽的 ECMAScript 规范约定。

对象的 [[Prototype]] 所指向的值,俗称“该对象的原型”。

如果您通过点 (obj.propName) 或方括号 (obj[‘propName’]) 表示法访问属性,并且对象没有直接具有这样的属性(即 自己的属性,可通过 {3 检查}),运行时会在 [[Prototype]] 引用的对象上查找具有该名称的属性。如果 [[Prototype]] also 没有这样的属性,则依次检查其 [[Prototype]],依此类推。这样,原始对象的 原型链 会一直走下去,直到找到匹配项,或者到达其末端。原型链的顶部是 null 值。

现代 JavaScript 实现允许通过以下方式对 [[Prototype]] 进行读取和/或写入访问:

new 运算符(在构造函数返回的默认对象上配置原型链)、extends 关键字(使用类语法时配置原型链)、Object.create 将提供的参数设置为 [[Prototype]] 的生成的对象,Object.getPrototypeOf 和 Object.setPrototypeOf(在对象创建后获取/设置 [[Prototype]]),以及名为 proto 的标准化访问器(即 getter/setter)属性(类似于 4.)

Object.getPrototypeOf 和 Object.setPrototypeOf 优于 proto,部分原因是当对象的原型为 null 时 o.proto is unusual 的行为。

对象的 [[Prototype]] 最初是在对象创建期间设置的。

如果您通过 new Func() 创建新对象,则默认情况下,该对象的 [[Prototype]] 将设置为 Func.prototype 引用的对象。

因此请注意,所有可与 new 运算符一起使用的类和函数,除了它们自己的 [[Prototype]] 内部槽外,还有一个名为 .prototype 的属性。这种双重用途“原型”一词是该语言新手无休止的困惑的根源。

将 new 与构造函数一起使用可以让我们模拟 JavaScript 中的经典继承;尽管 JavaScript 的继承系统——正如我们所见——是原型的,而不是基于类的。

在将类语法引入 JavaScript 之前,构造函数是模拟类的唯一方法。我们可以将构造函数的 .prototype 属性引用的对象的属性视为共享成员; IE。每个实例都相同的成员。在基于类的系统中,每个实例的方法都以相同的方式实现,因此方法在概念上被添加到 .prototype 属性中;但是,对象的字段是特定于实例的,因此在构造过程中会添加到对象本身。

如果没有类语法,开发人员必须手动配置原型链以实现与经典继承类似的功能。这导致了实现这一目标的不同方法的优势。

这是一种方法:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
  child.prototype = Object.create(parent.prototype)
  child.prototype.constructor = child
  return child;
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

…这是另一种方式:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
    function tmp() {}
    tmp.prototype = parent.prototype
    const proto = new tmp()
    proto.constructor = child
    child.prototype = proto
    return child
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

ES2015 中引入的类语法通过提供 extends 作为“一种真正的方式”来配置原型链以模拟 JavaScript 中的经典继承,从而简化了事情。

因此,类似于上面的代码,如果您使用类语法创建一个新对象,如下所示:

class Parent { inheritedMethod() { return 'this is inherited' } }
class Child extends Parent {}

const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

…结果对象的 [[Prototype]] 将设置为 Parent 的实例,其 [[Prototype]] 又是 Parent.prototype。

最后,如果您通过 Object.create(foo) 创建新对象,则生成的对象的 [[Prototype]] 将设置为 foo。

答3:

保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com

这是一个非常简单的基于原型的对象模型,在解释过程中将被视为示例,尚无评论:

function Person(name){
    this.name = name;
}
Person.prototype.getName = function(){
    console.log(this.name);
}
var person = new Person("George");

在了解原型概念之前,我们必须考虑一些关键点。

1- JavaScript 函数的实际工作原理:

要迈出第一步,我们必须弄清楚 JavaScript 函数实际上是如何工作的,作为在其中使用 this 关键字的类函数,或者只是作为带有参数的常规函数,它的作用和它返回什么。

假设我们要创建一个 Person 对象模型。但在这一步中,我将尝试在不使用 prototype 和 new 关键字的情况下做同样的事情。

因此,在这一步中,functions、objects 和 this 关键字就是我们所拥有的。

第一个问题是如何在不使用 new 关键字的情况下使用 this 关键字。

因此,要回答这个问题,假设我们有一个空对象,以及两个函数,例如:

var person = {};
function Person(name){  this.name = name;  }

function getName(){
    console.log(this.name);
}

现在不使用 new 关键字我们如何使用这些功能。所以 JavaScript 有 3 种不同的方法来做到这一点:

一个。第一种方法是将函数作为常规函数调用:

Person("George");
getName();//would print the "George" in the console

在这种情况下,这将是当前上下文对象,通常是浏览器中的全局 window 对象或 Node.js 中的 GLOBAL。这意味着我们将拥有浏览器中的 window.name 或 Node.js 中的 GLOBAL.name,其值为“George”。

湾。我们可以将它们附加到一个对象上,作为它的属性

-最简单的方法是修改空的 person 对象,例如:

person.Person = Person;
person.getName = getName;

这样我们就可以这样称呼它们:

person.Person("George");
person.getName();// -->"George"

现在 person 对象就像:

Object {Person: function, getName: function, name: "George"}

-将属性附加到对象的另一种方法是使用该对象的 prototype,该对象可以在任何名称为 proto 的 JavaScript 对象中找到,我已尝试对其进行解释关于摘要部分。所以我们可以通过这样做得到类似的结果:

person.__proto__.Person = Person;
person.__proto__.getName = getName;

但是这样我们实际上正在做的是修改 Object.prototype,因为每当我们使用文字 ({ … }) 创建 JavaScript 对象时,它都是基于 Object.prototype 创建的,这意味着它作为一个名为 proto 的属性附加到新创建的对象,所以如果我们改变它,就像我们在之前的代码片段中所做的那样,所有的 JavaScript 对象都会被改变,这不是一个好的做法。那么现在更好的做法是什么:

person.__proto__ = {
    Person: Person,
    getName: getName
};

而现在其他的对象都安然无恙,但似乎还是不是一个好的做法。所以我们还有一个解决方案,但要使用这个解决方案,我们应该回到创建 person 对象的那一行代码 (var person = {}😉,然后将其更改为:

var propertiesObject = {
    Person: Person,
    getName: getName
};
var person = Object.create(propertiesObject);

它所做的是创建一个新的 JavaScript Object 并将 propertiesObject 附加到 proto 属性。所以要确保你能做到:

console.log(person.__proto__===propertiesObject); //true

但这里的棘手点是您可以访问 person 对象第一级 proto 中定义的所有属性(阅读摘要部分了解更多详细信息)。

正如您所看到的,使用这两种方式中的任何一种 this 都会准确地指向 person 对象。

C。 JavaScript 有另一种方式为函数提供 this,即使用 call 或 apply 来调用函数。

apply() 方法使用给定的 this 值和作为数组(或类似数组的对象)提供的参数调用函数。

call() 方法使用给定的 this 值和单独提供的参数调用函数。

这种我最喜欢的方式,我们可以轻松地调用我们的函数,例如:

Person.call(person, "George");

或者

//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);

getName.call(person);   
getName.apply(person);

这 3 种方法是找出 .prototype 功能的重要初始步骤。

2-新关键字如何工作?

这是了解 .prototype 功能的第二步。这是我用来模拟该过程的方法:

function Person(name){  this.name = name;  }
my_person_prototype = { getName: function(){ console.log(this.name); } };

在这一部分中,我将尝试执行 JavaScript 所采取的所有步骤,而不使用 new 关键字和 prototype,当您使用 new 关键字时。所以当我们做 new Person(“George”) 时,Person 函数作为构造函数,这些是 JavaScript 所做的,一一对应:

一个。首先,它创建一个空对象,基本上是一个空哈希,例如:

var newObject = {};

湾。 JavaScript 的下一步是将所有原型对象附加到新创建的对象

我们这里有 my_person_prototype 类似于原型对象。

for(var key in my_person_prototype){
    newObject[key] = my_person_prototype[key];
}

这不是 JavaScript 实际附加原型中定义的属性的方式。实际方式与原型链概念有关。

一个。 & b。而不是这两个步骤,您可以通过执行以下操作获得完全相同的结果:

var newObject = Object.create(my_person_prototype);
//here you can check out the __proto__ attribute
console.log(newObject.__proto__ === my_person_prototype); //true
//and also check if you have access to your desired properties
console.log(typeof newObject.getName);//"function"

现在我们可以在 my_person_prototype 中调用 getName 函数:

newObject.getName();

C。然后它将该对象提供给构造函数,

我们可以用我们的样本来做到这一点,比如:

Person.call(newObject, "George");

或者

Person.apply(newObject, ["George"]);

然后构造函数可以做它想做的任何事情,因为构造函数内部的 this 是刚刚创建的对象。

现在是模拟其他步骤之前的最终结果: Object {name: “George”}

概括:

基本上,当您在函数上使用 new 关键字时,您正在调用该函数并且该函数用作构造函数,所以当您说:

new FunctionName()

JavaScript 在内部创建一个对象,一个空哈希,然后将该对象提供给构造函数,然后构造函数可以做任何它想做的事情,因为构造函数内部的 this 是刚刚创建的对象,然后如果您没有在函数中使用 return 语句,或者如果您在函数体的末尾放置了 return undefined;,它当然会为您提供该对象。

所以当 JavaScript 在一个对象上查找一个属性时,它做的第一件事就是在那个对象上查找它。然后有一个秘密属性 [[prototype]],我们通常拥有它,就像 proto 一样,该属性是 JavaScript 接下来要研究的。而当它通过 proto 看时,只要它又是另一个 JavaScript 对象,它就有自己的 proto 属性,它会一直向上直到它到达下一个 proto 为空的点。要点是 JavaScript 中其 proto 属性为 null 的唯一对象是 Object.prototype 对象:

console.log(Object.prototype.__proto__===null);//true

这就是 JavaScript 中继承的工作原理。

https://i.stack.imgur.com/JnpBV.png

换句话说,当你在一个函数上有一个原型属性并且你在它上面调用一个 new 时,在 JavaScript 完成查看新创建的对象的属性之后,它会去查看函数的 .prototype 并且这也有可能对象有自己的内部原型。等等。

答4:

huntsbot.com提供全网独家一站式外包任务、远程工作、创意产品分享与订阅服务!

prototype 允许您创建课程。如果你不使用 prototype 那么它会变成一个静态的。

这是一个简短的例子。

var obj = new Object();
obj.test = function() { alert('Hello?'); };

在上述情况下,您有静态函数调用测试。此函数只能由 obj.test 访问,您可以将 obj 想象为一个类。

在下面的代码中

function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

obj 已成为现在可以实例化的类。可以存在多个 obj 实例,并且它们都具有 test 函数。

以上是我的理解。我正在把它变成一个社区维基,所以如果我错了,人们可以纠正我。

-1:prototype 是构造函数的属性,而不是实例,即您的代码是错误的!也许您的意思是对象的非标准属性 __proto__,但那是完全不同的野兽......

答5:

HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com

原型之七公案

西罗桑在冥思之后下到火狐山上,心神清明平静。

然而,他的手却是不动声色,自己抓起一把毛笔,记下了下面的笔记。

0)两种不同的东西可以称为“原型”:

原型属性,如 obj.prototype

原型内部属性,在 ES5 中表示为 [[Prototype]]。它可以通过 ES5 Object.getPrototypeOf() 来检索。 Firefox 可以通过 proto 属性作为扩展来访问它。 ES6 现在提到 proto 的一些可选要求。

1)这些概念的存在是为了回答这个问题:

当我做obj.property时,JS在哪里寻找.property?

直观地说,经典继承应该会影响属性查找。

proto 用于点。在 obj.property 中进行属性查找。

.prototype 不直接用于查找,仅用于间接查找,因为它在使用 new 创建对象时确定 proto

查找顺序为:

使用 obj.p = … 或 Object.defineProperty(obj, …) 添加的 obj 属性

obj.proto 的属性

obj.proto.proto 的属性等等

如果某些 proto 为 null,则返回 undefined。

这就是所谓的原型链。

您可以避免使用 obj.hasOwnProperty(‘key’) 和 Object.getOwnPropertyNames(f) 进行 . 查找

  1. 设置 obj.proto 的方法主要有两种:

new: var F = function() {} var f = new F() then new 已设置: f.proto === F.prototype 这是使用 .prototype 的地方。

Object.create:f = Object.create(proto) 设置:f.proto === proto

4)代码:

var F = function(i) { this.i = i }
var f = new F(1)

对应于下图(省略了一些 Number 内容):

(Function)       (  F  )                                      (f)----->(1)
 |  ^             | | ^                                        |   i    |
 |  |             | | |                                        |        |
 |  |             | | +-------------------------+              |        |
 |  |constructor  | |                           |              |        |
 |  |             | +--------------+            |              |        |
 |  |             |                |            |              |        |
 |  |             |                |            |              |        |
 |[[Prototype]]   |[[Prototype]]   |prototype   |constructor   |[[Prototype]]
 |  |             |                |            |              |        |
 |  |             |                |            |              |        |
 |  |             |                | +----------+              |        |
 |  |             |                | |                         |        |
 |  |             |                | | +-----------------------+        |
 |  |             |                | | |                                |
 v  |             v                v | v                                |
(Function.prototype)              (F.prototype)                         |
 |                                 |                                    |
 |                                 |                                    |
 |[[Prototype]]                    |[[Prototype]]          [[Prototype]]|
 |                                 |                                    |
 |                                 |                                    |
 | +-------------------------------+                                    |
 | |                                                                    |
 v v                                                                    v
(Object.prototype)                                       (Number.prototype)
 | | ^
 | | |
 | | +---------------------------+
 | |                             |
 | +--------------+              |
 |                |              |
 |                |              |
 |[[Prototype]]   |constructor   |prototype
 |                |              |
 |                |              |
 |                | -------------+
 |                | |
 v                v |
(null)           (Object)

此图显示了许多语言预定义的对象节点:

无效的

目的

对象.原型

功能

函数原型

1

Number.prototype(可以通过 (1).proto 找到,括号必须满足语法)

我们的 2 行代码只创建了以下新对象:

F

F

F.原型

i 现在是 f 的属性,因为当您这样做时:

var f = new F(1)

它评估 F,其中 this 是 new 将返回的值,然后将其分配给 f。

  1. .constructor 通常来自 F.prototype 通过 . 查找:
f.constructor === F
!f.hasOwnProperty('constructor')
Object.getPrototypeOf(f) === F.prototype
F.prototype.hasOwnProperty('constructor')
F.prototype.constructor === f.constructor

当我们编写 f.constructor 时,JavaScript 将 . 查找为:

f 没有 .constructor

f.proto === F.prototype 有 .constructor === F,所以拿它

结果 f.constructor == F 直观上是正确的,因为 F 用于构造 f,例如设置字段,很像经典的 OOP 语言。

6)经典的继承语法可以通过操作原型链来实现。

ES6 添加了 class 和 extends 关键字,它们主要是以前可能的原型操作疯狂的语法糖。

class C {
    constructor(i) {
        this.i = i
    }
    inc() {
        return this.i + 1
    }
}

class D extends C {
    constructor(i) {
        super(i)
    }
    inc2() {
        return this.i + 2
    }
}

// Inheritance syntax works as expected.
c = new C(1)
c.inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3

// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype

// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc

// Class variables
// No ES6 syntax sugar apparently:
// http://stackoverflow.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined

没有所有预定义对象的简化图:

(c)----->(1)
 |   i
 |
 |
 |[[Prototype]]
 |
 |
 v    __proto__
(C)<--------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |[[Prototype]] 
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|[[Prototype]]    (D.prototype)--------> (inc2 function object)
| |                |             inc2
| |                |
| |                |[[Prototype]]
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)------->(inc function object)
|                inc
v
Function.prototype

让我们花点时间研究一下以下是如何工作的:

c = new C(1)
c.inc() === 2

第一行将 c.i 设置为 1,如“4)”中所述。

在第二行,当我们这样做时:

c.inc()

.inc 是通过 [[Prototype]] 链找到的:c -> C -> C.prototype -> inc

当我们在 Javascript 中将函数调用为 XY() 时,JavaScript 会在 Y() 函数调用中自动将其设置为等于 X!

完全相同的逻辑也解释了 d.inc 和 d.inc2。

这篇文章https://javascript.info/class#not-just-a-syntax-sugar提到了值得了解的class的进一步影响。如果没有 class 关键字,其中一些可能无法实现(TODO 检查哪个):

[[FunctionKind]]:“classConstructor”,强制用new调用构造函数:ES6类构造函数不能作为普通函数调用的原因是什么?

类方法是不可枚举的。可以使用 Object.defineProperty 来完成。

类总是使用严格的。可以通过对每个函数显式使用严格来完成,这无疑是乏味的。

答6:

huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求

读完这个帖子后,我对 JavaScript Prototype Chain 感到困惑,然后我找到了这些图表

https://i.stack.imgur.com/rcGmc.png

这是一个通过原型链显示 JavaScript 继承的清晰图表

http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/

这个包含一个带有代码的示例和几个漂亮的图表。

原型链最终回退到 Object.prototype。原型链可以在技术上随心所欲地扩展,每次通过将子类的原型设置为父类的对象。

希望对你理解 JavaScript Prototype Chain 也有帮助。

答7:

huntsbot.com – 高效赚钱,自由工作

每个对象都有一个内部属性 [[Prototype]],将它链接到另一个对象:

object [[Prototype]] → anotherObject

在传统 javascript 中,链接对象是函数的 prototype 属性:

object [[Prototype]] → aFunction.prototype

一些环境将 [[Prototype]] 公开为 proto

anObject.__proto__ === anotherObject

在创建对象时创建 [[Prototype]] 链接。

// (1) Object.create:
var object = Object.create(anotherObject)
// object.__proto__ = anotherObject

// (2) ES6 object initializer:
var object = { __proto__: anotherObject };
// object.__proto__ = anotherObject

// (3) Traditional JavaScript:
var object = new aFunction;
// object.__proto__ = aFunction.prototype

所以这些语句是等价的:

var object = Object.create(Object.prototype);
var object = { __proto__: Object.prototype }; // ES6 only
var object = new Object;

您实际上无法在 new 语句中看到链接目标 (Object.prototype);相反,目标是由构造函数(Object)隐含的。

记住:

每个对象都有一个链接,[[Prototype]],有时暴露为 proto

每个函数都有一个原型属性,最初持有一个空对象。

使用 new 创建的对象链接到其构造函数的原型属性。

如果一个函数从不用作构造函数,它的原型属性将不被使用。

如果您不需要构造函数,请使用 Object.create 而不是 new。

修订版 5 删除了一些有用的信息,包括有关 Object.create() 的信息。请参阅revision 4。

@Palec 我应该添加什么?

IMO 至少链接到 Object.create() docs,@sam。到 __proto__ 和 Object.prototype 的链接将是很好的增强。我喜欢你关于原型如何与构造函数和 Object.create() 一起工作的示例,但它们可能是你想要摆脱的冗长且不太相关的部分。

从所有讨论中我得到什么(来自经典继承)如果我创建构造函数并尝试使用 new 运算符创建它的实例,我只会获得附加到原型对象的方法和属性,因此有必要附加所有方法如果我们想继承原型对象的属性,对吗?

答8:

一个优秀的自由职业者,应该有对需求敏感和精准需求捕获的能力,而huntsbot.com提供了这个机会

Javascript没有通常意义上的继承,但它有原型链。

原型链

如果在对象中找不到对象的成员,它会在原型链中查找它。链由其他对象组成。可以使用 proto 变量访问给定实例的原型。每个对象都有一个,因为 javascript 中的类和实例之间没有区别。

将函数/变量添加到原型的优点是它必须只在内存中一次,而不是每个实例。

它对于继承也很有用,因为原型链可以包含许多其他对象。

FF 和 Chrome 支持 proto,但不支持 IE 和 Opera。

Georg,请澄清一个菜鸟 - “javascript 中的类和实例之间没有区别。” - 你能详细说明吗?这是如何运作的?

从所有讨论中我得到什么(来自经典继承)如果我创建构造函数并尝试使用 new 运算符创建它的实例,我只会获得附加到原型对象的方法和属性,因此有必要附加所有方法如果我们想继承原型对象的属性,对吗?

答9:

huntsbot.com – 程序员副业首选,一站式外包任务、远程工作、创意产品分享订阅平台。

这篇文章很长。但我相信它会清除您关于 JavaScript 继承的“原型”性质的大部分疑问。甚至更多。请阅读完整的文章。

JavaScript 基本上有两种数据类型

非对象

对象

非对象

以下是非对象数据类型

细绳

数(包括 NaN 和 Infinity)

布尔值(真,假)

不明确的

当您使用 typeof 运算符时,这些数据类型会返回以下内容

typeof “字符串文字” (或包含字符串文字的变量) === ‘string’

typeof 5(或任何数字文字或包含数字文字或 NaN 或 Infynity 的变量)=== ‘number’

typeof true (或 false 或包含 true 或 false 的变量) === ‘boolean’

typeof undefined(或未定义的变量或包含未定义的变量)=== ‘undefined’

字符串、数字和布尔数据类型可以表示为对象和非对象。当它们表示为对象时,它们的 typeof 始终为 === ‘object’。一旦我们了解了对象数据类型,我们将回到这一点。

对象

对象数据类型可以进一步分为两种类型

函数类型对象 非函数类型对象

Function 类型对象是使用 typeof 运算符返回字符串 ‘function’ 的对象。所有用户定义的函数和所有可以使用 new 运算符创建新对象的 JavaScript 内置对象都属于这一类。例如。

目的

细绳

数字

布尔值

大批

类型化数组

正则表达式

功能

所有其他可以使用 new 运算符创建新对象的内置对象

function UserDefinedFunction(){ /*用户定义代码 */ }

所以,typeof(Object) === typeof(String) === typeof(Number) === typeof(Boolean) === typeof(Array) === typeof(RegExp) === typeof(Function) == = typeof(UserDefinedFunction) === ‘函数’

所有的 Function 类型对象实际上都是内置 JavaScript 对象 Function 的实例(包括 Function 对象,即它是递归定义的)。就好像这些对象已按以下方式定义

var Object= new Function ([native code for object Object])
var String= new Function ([native code for object String])
var Number= new Function ([native code for object Number])
var Boolean= new Function ([native code for object Boolean])
var Array= new Function ([native code for object Array])
var RegExp= new Function ([native code for object RegExp])
var Function= new Function ([native code  for object Function])
var UserDefinedFunction= new Function ("user defined code")

如前所述,Function 类型对象可以使用 new 运算符进一步创建新对象。例如,可以使用 Object、String、Number、Boolean、Array、RegExp 或 UserDefinedFunction 类型的对象创建

var a=new Object() or var a=Object() or var a={} //Create object of type Object
var a=new String() //Create object of type String
var a=new Number() //Create object of type Number
var a=new Boolean() //Create object of type Boolean
var a=new Array() or var a=Array() or var a=[]  //Create object of type Array
var a=new RegExp() or var a=RegExp() //Create object of type RegExp
var a=new UserDefinedFunction() 

这样创建的对象都是非函数类型的对象,并返回它们的 typeof===‘object’。在所有这些情况下,对象“a”不能使用 operator new 进一步创建对象。所以以下是错误的

var b=new a() //error. a is not typeof==='function'

内置对象 Math 是 typeof===‘object’。因此,新运算符不能创建 Math 类型的新对象。

var b=new Math() //error. Math is not typeof==='function'

另请注意,Object、Array 和 RegExp 函数甚至可以在不使用 operator new 的情况下创建新对象。然而后面的没有。

var a=String() // Create a new Non Object string. returns a typeof==='string' 
var a=Number() // Create a new Non Object Number. returns a typeof==='number'
var a=Boolean() //Create a new Non Object Boolean. returns a typeof==='boolean'

用户定义的函数是特殊情况。

var a=UserDefinedFunction() //may or may not create an object of type UserDefinedFunction() based on how it is defined.

由于 Function 类型的对象可以创建新对象,因此它们也称为构造函数。

每个构造函数/函数(无论是内置的还是用户定义的)在自动定义时都有一个名为“原型”的属性,其值默认设置为对象。该对象本身有一个名为“constructor”的属性,默认情况下它引用回 Constructor/Function 。

例如当我们定义一个函数时

function UserDefinedFunction()
{
}

以下自动发生

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

这个“原型”属性只存在于函数类型对象中(而不存在于非函数类型对象中)。

这是因为当一个新对象被创建(使用 new 操作符)时,它会从构造函数的当前原型对象继承所有属性和方法,即在新创建的对象中创建一个内部引用,该对象引用构造函数的当前原型对象所引用的对象。

在对象中创建的用于引用继承属性的“内部引用”称为对象的原型(它引用了构造函数的“原型”属性所引用的对象,但与它不同)。对于任何对象(函数或非函数),可以使用 Object.getPrototypeOf() 方法检索。使用这种方法可以追踪对象的原型链。

此外,创建的每个对象(函数类型或非函数类型)都有一个“构造函数”属性,该属性继承自构造函数的原型属性引用的对象。默认情况下,此“构造函数”属性引用创建它的构造函数(如果构造函数的默认“原型”未更改)。

对于所有 Function 类型对象,构造函数始终是 function Function(){}

对于非函数类型对象(例如 Javascript Built in Math 对象),构造函数是创建它的函数。对于 Math 对象,它是函数 Object(){}。

如果没有任何支持代码,上面解释的所有概念可能有点难以理解。请逐行阅读以下代码以了解该概念。尝试执行它以获得更好的理解。

function UserDefinedFunction()
{ 

} 

/* creating the above function automatically does the following as mentioned earlier

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

*/


var newObj_1=new UserDefinedFunction()

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays true

alert(newObj_1.constructor) //Displays function UserDefinedFunction

//Create a new property in UserDefinedFunction.prototype object

UserDefinedFunction.prototype.TestProperty="test"

alert(newObj_1.TestProperty) //Displays "test"

alert(Object.getPrototypeOf(newObj_1).TestProperty)// Displays "test"

//Create a new Object

var objA = {
        property1 : "Property1",
        constructor:Array

}


//assign a new object to UserDefinedFunction.prototype
UserDefinedFunction.prototype=objA

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays false. The object referenced by UserDefinedFunction.prototype has changed

//The internal reference does not change
alert(newObj_1.constructor) // This shall still Display function UserDefinedFunction

alert(newObj_1.TestProperty) //This shall still Display "test" 

alert(Object.getPrototypeOf(newObj_1).TestProperty) //This shall still Display "test"


//Create another object of type UserDefinedFunction
var newObj_2= new UserDefinedFunction();

alert(Object.getPrototypeOf(newObj_2)===objA) //Displays true.

alert(newObj_2.constructor) //Displays function Array()

alert(newObj_2.property1) //Displays "Property1"

alert(Object.getPrototypeOf(newObj_2).property1) //Displays "Property1"

//Create a new property in objA
objA.property2="property2"

alert(objA.property2) //Displays "Property2"

alert(UserDefinedFunction.prototype.property2) //Displays "Property2"

alert(newObj_2.property2) // Displays Property2

alert(Object.getPrototypeOf(newObj_2).property2) //Displays  "Property2"

每个对象的原型链最终都追溯到 Object.prototype(它本身没有任何原型对象)。以下代码可用于跟踪对象的原型链

var o=Starting object;

do {
    alert(o + "\n" + Object.getOwnPropertyNames(o))

}while(o=Object.getPrototypeOf(o))

各种对象的原型链如下所示。

每个 Function 对象(包括内置的 Function 对象)-> Function.prototype -> Object.prototype -> null

简单对象(由 new Object() 或 {} 创建,包括内置的 Math 对象)-> Object.prototype -> null

使用 new 或 Object.create 创建的对象 -> 一个或多个原型链 -> Object.prototype -> null

要创建没有任何原型的对象,请使用以下命令:

var o=Object.create(null)
alert(Object.getPrototypeOf(o)) //Displays null

有人可能认为将 Constructor 的原型属性设置为 null 会创建一个具有 null 原型的对象。然而,在这种情况下,新创建的对象的原型设置为 Object.prototype,其构造函数设置为函数 Object。下面的代码证明了这一点

function UserDefinedFunction(){}
UserDefinedFunction.prototype=null// Can be set to any non object value (number,string,undefined etc.)

var o=new UserDefinedFunction()
alert(Object.getPrototypeOf(o)==Object.prototype)   //Displays true
alert(o.constructor)    //Displays Function Object

以下是本文的摘要

对象有函数类型和非函数类型两种

只有 Function 类型的对象可以使用 new 运算符创建新对象。这样创建的对象是非函数类型的对象。 Non Function 类型的对象不能使用 operator new 进一步创建对象。

默认情况下,所有函数类型对象都有一个“原型”属性。这个“原型”属性引用了一个具有“构造函数”属性的对象,该属性默认引用了函数类型对象本身。

所有对象(函数类型和非函数类型)都有一个“构造函数”属性,默认情况下引用创建它的函数类型对象/构造函数。

在内部创建的每个对象都引用由创建它的构造函数的“原型”属性引用的对象。这个对象被称为创建对象的原型(它不同于它所引用的函数类型对象的“原型”属性)。这样创建的对象可以直接访问构造函数的“原型”属性(在对象创建时)引用的对象中定义的方法和属性。

可以使用 Object.getPrototypeOf() 方法检索对象的原型(以及继承的属性名称)。事实上,这种方法可以用于导航对象的整个原型链。

每个对象的原型链最终都追溯到 Object.prototype(除非对象是使用 Object.create(null) 创建的,在这种情况下对象没有原型)。

typeof(new Array())===‘object’ 是语言设计的,而不是 Douglas Crockford 指出的错误

将构造函数的原型属性设置为 null(或 undefined,number,true,false,string)不应创建具有 null 原型的对象。在这种情况下,新创建的对象的原型设置为 Object.prototype,其构造函数设置为函数 Object。

希望这可以帮助。

答10:

huntsbot.com精选全球7大洲远程工作机会,涵盖各领域,帮助想要远程工作的数字游民们能更精准、更高效的找到对方。

将原型链分为两类可能会有所帮助。

考虑构造函数:

 function Person() {}

Object.getPrototypeOf(Person) 的值是一个函数。实际上是Function.prototype。由于 Person 是作为函数创建的,因此它与所有函数共享相同的原型函数对象。它与 Person.proto 相同,但不应使用该属性。无论如何,使用 Object.getPrototypeOf(Person),您可以有效地爬上所谓的原型链的阶梯。

向上的链条如下所示:

Person → Function.prototype → Object.prototype(终点)

重要的是,此原型链与 Person 可以构造的对象几乎没有关系。这些构造的对象有自己的原型链,并且这个链可能没有与上面提到的相同的近亲。

以这个对象为例:

var p = new Person();

p 与 Person 没有直接的原型链关系。他们的关系是不同的。 p 对象有自己的原型链。使用 Object.getPrototypeOf,您会发现链如下:

p → Person.prototype → Object.prototype(终点)

此链中没有函数对象(尽管可能如此)。

所以 Person 似乎与两种链条有关,它们过着自己的生活。要从一条链“跳转”到另一条链,请使用:

.prototype:从构造函数链跳转到创建对象链。因此,此属性仅针对函数对象定义(因为 new 只能用于函数)。 .constructor:从创建对象的链跳转到构造函数的链。

这是所涉及的两个原型链的可视化演示,以列表示:

https://i.stack.imgur.com/FPPdI.png

总结一下:

原型属性不提供主体原型链的信息,而是主体创建的对象的信息。

毫不奇怪,属性 prototype 的名称会导致混淆。如果将该属性命名为 prototypeOfConstructedInstances 或类似的名称,可能会更清楚。

你可以在两个原型链之间来回跳转:

Person.prototype.constructor === Person

通过将不同的对象显式分配给 prototype 属性可以打破这种对称性(稍后会详细介绍)。

创建一个函数,获取两个对象

Person.prototype 是在创建函数 Person 的同时创建的对象。它有 Person 作为构造函数,即使该构造函数实际上还没有执行。所以同时创建了两个对象:

函数 Person 本身 当函数被调用为构造函数时将充当原型的对象

两者都是对象,但它们具有不同的作用:函数对象构造,而另一个对象代表函数将构造的任何对象的原型。原型对象将成为其原型链中构造对象的父对象。

由于函数也是一个对象,它在自己的原型链中也有自己的父级,但请记住,这两个链是关于不同事物的。

以下是一些有助于理解问题的等式 - 所有这些都打印 true:

函数人(){}; // 这是构造函数(函数对象)的原型链信息:console.log(Object.getPrototypeOf(Person) === Function.prototype); // 在同一层次结构中更进一步:console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype); console.log(Object.getPrototypeOf(Object.prototype) === null); console.log(Person.proto === Function.prototype); // 这里我们换车道,看构造函数console.log(Person.constructor === Function); console.log(Person instanceof Function); // Person.prototype 是由 Person 创建的(在创建时) // 这里我们来回交换通道:console.log(Person.prototype.constructor === Person); // 虽然它不是它的实例:console.log(!(Person.prototype instanceof Person)); // 实例是由构造函数创建的对象: var p = new Person(); // 与为构造函数显示的内容类似,这里我们对构造函数创建的对象具有 // 相同的内容:console.log(Object.getPrototypeOf§ === Person.prototype); console.log(p.proto === Person.prototype); // 这里我们交换通道,并查看构造函数 console.log(p.constructor === Person); console.log(p instanceof Person);

向原型链添加关卡

尽管在创建构造函数时会创建原型对象,但您可以忽略该对象,并分配另一个对象,该对象应用作该构造函数创建的任何后续实例的原型。

例如:

function Thief() { }
var p = new Person();
Thief.prototype = p; // this determines the prototype for any new Thief objects:
var t = new Thief();

现在 t 的原型链比 p 的原型链长一步:

t → p → Person.prototype → Object.prototype(终点)

另一个原型链不再:Thief 和 Person 是在其原型链中共享同一个父级的兄弟:

Person}     Thief  } → Function.prototype → Object.prototype(终点)

然后可以将之前显示的图形扩展到此(原始 Thief.prototype 被省略):

https://i.stack.imgur.com/m5DXc.png

蓝线代表原型链,其他彩色线代表其他关系:

对象与其构造函数之间

在构造函数和将用于构造对象的原型对象之间

答11:

打造属于自己的副业,开启自由职业之旅,从huntsbot.com开始!

prototypal 继承的概念对于许多开发人员来说是最复杂的概念之一。让我们尝试了解问题的根源以更好地理解 prototypal inheritance。让我们从 plain 函数开始。

https://i.stack.imgur.com/BsHT0.png

如果我们在 Tree function 上使用 new 运算符,我们将其称为 constructor 函数。

https://i.stack.imgur.com/cU6Qh.png

每个 JavaScript 函数都有一个 prototype。当您记录 Tree.prototype 时,您会得到…

https://i.stack.imgur.com/Xop8c.png

如果您查看上面的 console.log() 输出,您可以看到 Tree.prototype 上的构造函数属性和 proto 属性。 proto 表示此 function 所基于的 prototype,由于这只是一个没有设置 inheritance 的普通 JavaScript function,它指的是刚刚内置的 Object prototype到 JavaScript…

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

这有 .toString, .toValue, .hasOwnProperty 之类的东西…

我的 mozilla 带来的 proto 已弃用,取而代之的是 Object.getPrototypeOf 方法以获取 object’s prototype。

https://i.stack.imgur.com/GtcJO.png

Object.getPrototypeOf(Tree.prototype); // Object {} 

让我们向 Tree prototype 添加一个方法。

https://i.stack.imgur.com/BsHT0.png

我们已修改 Root 并向其添加了 function 分支。

https://i.stack.imgur.com/cU6Qh.png

这意味着当您创建 Tree 的 instance 时,您可以调用它的 branch 方法。

https://i.stack.imgur.com/Xop8c.png

我们还可以将 primitives 或 objects 添加到 Prototype。

https://i.stack.imgur.com/GtcJO.png

让我们将 child-tree 添加到我们的 Tree。

https://i.stack.imgur.com/ggFON.png

这里 Child 从 Tree 继承了它的 prototype,我们在这里所做的是使用 Object.create() 方法根据您传递的内容创建一个新对象,这里是 Tree.prototype。在这种情况下,我们所做的是将 Child 的原型设置为一个看起来与 Tree 原型相同的新对象。接下来我们设置 Child’s constructor to Child,如果我们不这样做,它将指向 Tree()。

https://i.stack.imgur.com/yiZcY.png

Child 现在有自己的 prototype,它的 proto 指向 Tree,Tree’s prototype 指向基 Object。

Child  
|
 \
  \
   Tree.prototype
   - branch
   |
   |
    \
     \
      Object.prototype
      -toString
      -valueOf
      -etc., etc.

现在您创建一个 Child 的 instance 并调用最初在 Tree 中可用的 branch。我们实际上还没有在 Child prototype 上定义我们的 branch。但是,在 Child 继承的 Root prototype 中。

https://i.stack.imgur.com/k6BNb.png

在 JS 中,一切都不是对象,一切都可以像对象一样。

Javascript 有像 strings, number, booleans, undefined, null. 这样的原语它们不是 object(i.e reference types),但肯定可以像 object 一样工作。让我们看一个例子。

https://i.stack.imgur.com/WVjiv.png

在此清单的第一行中,将 primitive 字符串值分配给 name。第二行将 name 视为 object 并使用点表示法调用 charAt(0)。

这就是幕后发生的事情: // JavaScript 引擎的作用

https://i.stack.imgur.com/l6MHc.png

String object 在被销毁之前仅存在于一个语句(称为 autoboxing 的过程)。让我们再次回到我们的 prototypal inheritance。

Javascript 支持通过基于原型的委托进行继承。

每个函数都有一个原型属性,它引用另一个对象。

从对象本身或通过原型链查找属性/函数(如果它不存在)

JS 中的 prototype 是一个对象,它 yields 指向另一个 object 的父级。 [ie…delegation] Delegation 表示如果您不能做某事,您会告诉其他人为您做这件事。

https://i.stack.imgur.com/W0NUA.png

https://jsfiddle.net/say0tzpL/1/

如果您查看上面的小提琴,dog 可以访问 toString 方法,但它在其中不可用,但可以通过委托给 Object.prototype 的原型链获得

https://i.stack.imgur.com/lWILf.png

如果您查看下面的内容,我们正在尝试访问每个 function 中都可用的 call 方法。

https://i.stack.imgur.com/iF4RN.png

https://jsfiddle.net/rknffckc/

如果您查看上面的小提琴,Profile 函数可以访问 call 方法,但它在其中不可用,但可以通过委托给 Function.prototype 的原型链获得

https://i.stack.imgur.com/Mijkj.png

注意: prototype 是函数构造函数的属性,而 proto 是从函数构造函数构造的对象的属性。每个函数都带有一个 prototype 属性,其值为空 object。当我们创建函数的实例时,我们会得到一个内部属性 [[Prototype]] 或 proto,其引用是函数 constructor 的原型。

https://i.stack.imgur.com/HvzDP.png

上图看起来有点复杂,但可以全面了解 prototype chaining 的工作原理。让我们慢慢来看看:

有两个实例 b1 和 b2,其构造函数是 Bar,父对象是 Foo,并且通过 Bar 和 Foo 具有来自原型链 identify 和 speak 的两个方法

https://i.stack.imgur.com/EllEL.png

https://jsfiddle.net/kbp7jr7n/

如果您查看上面的代码,我们有 Foo 具有方法 identify() 的构造函数和具有 speak 方法的 Bar 构造函数。我们创建两个 Bar 实例 b1 和 b2,其父类型为 Foo。现在在调用 Bar 的 speak 方法时,我们可以通过 prototype 链识别谁在调用说话。

https://i.stack.imgur.com/V7fH7.png

Bar 现在具有在其 prototype 中定义的 Foo 的所有方法。让我们进一步深入了解 Object.prototype 和 Function.prototype 以及它们之间的关系。如果查找 Foo 的构造函数,Bar 和 Object 是 Function constructor。

https://i.stack.imgur.com/wzzRu.png

Bar 的 prototype 是 Foo,Foo 的 prototype 是 Object,如果仔细观察,Foo 的 prototype 与 Object.prototype 相关。

https://i.stack.imgur.com/wEOxo.png

在我们关闭它之前,让我们在这里用一小段代码来总结以上所有内容。我们在这里使用 instanceof 运算符来检查 object 在其 prototype 链中是否具有 constructor 的 prototype 属性,该属性在下面总结了整个大图。

https://i.stack.imgur.com/n84uV.png

我希望这个添加的一些信息,我知道这可能很容易掌握…简而言之,它只是链接到对象的对象!!!!

Child now has its own prototype, its __proto__ points to Tree - 似乎是错误的。 __proto__ 指向 Function.prototype 而不是 Tree。

原文链接:https://www.huntsbot.com/qa/yOA4/how-does-javascript-prototype-work?lang=zh_CN&from=csdn

huntsbot.com – 程序员副业首选,一站式外包任务、远程工作、创意产品分享订阅平台。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值