js对象中自身声明的方法和属性与prototype声明的对象有什么差别?

发布于 2022-09-02 15:31:48 字数 660 浏览 22 评论 0

如题,
比如说这两个东东:

Array.isArray()  // Array.prototype.isArray() 这样就是错的
Array.prototype.map // Array.map 错的,没有这个属性

我们知道js对象中总会有个prototype的东东,利用它可以进行原型的继承等等一些高级操作。

可是问题来了,
1、除了使用prototype来为对象动态设置属性&方法外,啥时候用prototype呢?

2、使用prototype和不使用prototype有什么区别呢?

举个例子:

function People() {}
var p = new People();
p.name = 'Tom';
p // People {name: 'Tom'}
// p.prototype.age = 7; // 错误
People.prototype.age = 7;
p // People {name: 'Tom'}  // 木有age这个显式的属性,被加在__proto__中
p.age // 7
// p.prototype.age // 错误
People.prototype.age // 7

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

请别遗忘我 2022-09-09 15:31:48

prototype这个属性只有函数对象才有,具体的说就是构造函数具有.只要你声明定义了一个函数对象,这个prototype就会存在
对象实例是没有这个属性,__proto__是JS实现的自有属性,赋予对象实例的,通过__proto__这个属性指向这个对象实例的原型对象,这要通过new操作符构建的对象都有这个属性__proto__
prototype属性指向一个对象

当对一个对象的某个属性执行赋值操作的时候,
如果这个属性在当前对象上不存在的话,但在原型对象上存在且是只读的,那么这个赋值操作无效;

function People() {
}
Object.defineProperty(People.prototype,"name",{
    writable:false,
    value:'People.prototype'
});
var p = new People();
p.name = 'Tom';
console.log(p.name);//People.prototype
console.log(p.hasOwnProperty("name"));//false

如果这个属性在当前对象上不存在的话,但在原型对象上存在非只读,不过有set属性,那么属性不会添加到对象上,会调用set方法;

function People() {
}
Object.defineProperty(People.prototype,"name",{
    get: function() { return this.otherName },
    set: function(newValue) { this.otherName = newValue; },
});
var p = new People();
console.log(p.name);//undefine;
p.name = 'Tom';
console.log(p.name);//undefine;
console.log(p.otherName);//Tom;
console.log(p.hasOwnProperty("name"));//false
console.log(People.prototype.hasOwnProperty("name"));//true

否则就会在对象上添加这个属性

function People() {
}
Object.defineProperty(People.prototype,"name",{
    value:'People.prototype',
    writable:true
});
var p = new People();
p.name = 'Tom';
console.log(p.name);//Tom
console.log(People.prototype.name);//People.prototype
console.log(p.name===People.prototype.name);//false
console.log(p.hasOwnProperty("name"));//true

当从一个对象上读取某个属性值,将从当前对象属性开始沿着原型链查找这个这个属性,如果到Object.prototype还是没有这个属性,那么返回undefined

function People() {
}
Object.defineProperty(People.prototype,"name",{
    value:'People.prototype',
    writable:true
});
var p = new People();
console.log(p.name);//People.prototype
console.log(p.hasOwnProperty("name"));//false

console.log(People.prototype.name);//People.prototype
console.log(p.name===People.prototype.name);//true

console.log(p.age);//undefined
console.log(p.hasOwnProperty("age"));//false

除了你定义一个对象(类)时需要到prototype,其它情况一般不会对prototype直接操作

此岸叶落 2022-09-09 15:31:48

这个东西网上已经说得够多了

JS 用 function 来实现类,
类的 prototype 是其原型,
原型上的方法和属性会作用于这个类产生的对象,
但是如果对象有了自己的同名方法或属性,就会使用自己的而不是原型的。

原型方法或属性作用于所有对象(除非对象有自己的同名方法或属性)

我要还你自由 2022-09-09 15:31:48

在一般的语言里,比如 PHP。可以定义一个类

class Person{
   public $name;
   fucntion say() { echo $this->name;}
}

然后用这个类声明一个变量$p1 = new Person();,那么 p1 就会有一个属性叫做 name。而Person只是一个类的名字,不能当作变量来用比如$Person->name = 10; 就是错的。

可是在 javascript 里面,Personp1 都被认为是一个对象,而 没有 class 关键字这种声明的方式。那么,Person,p1 都是一个对象,所以可以定义新的属性比如Person.classname = "People";p1.name="Tom"。这些在javascript里面都很正常。那么我们如何完成像 PHP里面那种类的定义呢,js给出的解决方式就是 把PHP里 {public $name;fucntion say() { echo $this->name;}} 这一部分写到 Person 的一个特殊属性prototype里面,例如:

function Person(){ }
Person.prototype = {
    constructor:Person,
    name:"Tom",
    say: function() { console.log(this.name)}
}

这样p1 就有可以有p1.namep1.say() 了,可是 js里面存在的问题是这些属性是只读的而且是和其他其他该类的对象共享的(因为它其实是Person.prototype的属性,涉及原型链).如果你给p1.name = "John"赋一个新的值,其实不会影响到 Person.prototype.name,而是给 p1 增加了一个新的属性 name

var p1 = new Person();
var p2 = new Person();
console.log(p1);  // {}
console.log(p1.name); // "Tom"
p1.name = "John";
console.log(p1); // {name:"John"}
console.log(p2.name); // "Tom"

所以在js里面一般是一个对象的方法和一些常量放在其类的定义之中,其他属性其实是动态添加的。所以 js 里面一般这样写:

function Person(name){ this.name = name;}
Person.prototype = {
    constructor:Person,
    say: function() { console.log(this.name)}
}

至于第一个问题isArray放在哪里的问题,由于 Array 是一个对象,所以可以给其添加属性isArray。这一点儿js跟其他语言不同,比如在PHP里面 Person 不是一个变量名$Person->isPerson 是不合法的,当然如果是class Person { function isPerson(){}} 这样的代码有意义吗?所以说isPerson 这个函数其实不应该定义成类的方法(先不说静态方法),而是定义成独立于对象的全局方法。所以isPerson 应该是一个全局变量。但是全局变量么,你懂的,总是不好,给他选一个位置的话,在js里,那就放到Person的属性里吧(其他语言里一般定义成类的静态方法或是放到其他的命名空间或是模块里),这样就不会污染全局变量空间。

最后写个等价的 ES6 的 Person 定义可能更好理解;

class Person {
     constructor(name) { this.name = name;}
     say { console.log(this.name);}
}
var p1 = new Person("Tom");
p1.say();

Person.isPerson = function(p) { return p instanceof Person;}
窗影残 2022-09-09 15:31:48

1、除了使用prototype来为对象动态设置属性&方法外,啥时候用prototype呢?

答:因为成员方法的类型一般是引用类型function,引用类型一般占用空间大,所以我们一般提倡把成员方法写到原型上,因为当你new一个构造函数,生成的对象的从原型继承的属性都是指向同一个内存堆地址,从而达到节省内存的目的。

2、使用prototype和不使用prototype有什么区别呢?

答:prototype的目的就是节省内存空间,为什么这么说?同理,当你new一个构造函数,生成的对象的从原型继承的属性都是指向同一个内存堆地址,从而达到节省内存的目的。如果不写到原型上的话,当你new一个构造函数,new一次,这个成员方法就会在生成在堆里面一次,浪费内存。从而你就明白了原型继承的好处。

源来凯始玺欢你 2022-09-09 15:31:48

修改父类prototype上的属性会影响到后面子类,前提是子类没有该自身属性。

在风中等你 2022-09-09 15:31:48

可以理解为动态静态成员属性/方法 和 动态实例属性/方法

风苍溪 2022-09-09 15:31:48

个人观点: prototype上定义的方法一般是这个类(js里用function模拟)的实例共用的方法,比如你说的Array.prototype.map,因为任何一个数组都需要这个map功能,所以就给他定义在prototype上,这样的话这个类的每个实例都可以共享这个方法。对于isArray这样的方法,添加在prototype上就不太合适了,否则调用起来也是a.isArray(a) //假设a是一个数组。另外prototype是函数的属性,你的p.prototype.name的写法是不对的。另外使用和不使用prototype的区别,就是会不会被继承吧。。。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文