如题,
比如说这两个东东:
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
prototype
这个属性只有函数对象才有,具体的说就是构造函数具有.只要你声明定义了一个函数对象,这个prototype
就会存在对象实例是没有这个属性,
__proto__
是JS实现的自有属性,赋予对象实例的,通过__proto__
这个属性指向这个对象实例的原型对象,这要通过new操作符构建的对象都有这个属性__proto__
prototype
属性指向一个对象当对一个对象的某个属性执行赋值操作的时候,
如果这个属性在当前对象上不存在的话,但在原型对象上存在且是只读的,那么这个赋值操作无效;
如果这个属性在当前对象上不存在的话,但在原型对象上存在非只读,不过有set属性,那么属性不会添加到对象上,会调用set方法;
否则就会在对象上添加这个属性
当从一个对象上读取某个属性值,将从当前对象属性开始沿着原型链查找这个这个属性,如果到Object.prototype还是没有这个属性,那么返回undefined
除了你定义一个对象(类)时需要到prototype,其它情况一般不会对prototype直接操作
这个东西网上已经说得够多了
JS 用 function 来实现类,
类的 prototype 是其原型,
原型上的方法和属性会作用于这个类产生的对象,
但是如果对象有了自己的同名方法或属性,就会使用自己的而不是原型的。
原型方法或属性作用于所有对象(除非对象有自己的同名方法或属性)
在一般的语言里,比如 PHP。可以定义一个类
然后用这个类声明一个变量
$p1 = new Person();
,那么 p1 就会有一个属性叫做 name。而Person只是一个类的名字,不能当作变量来用比如$Person->name = 10;
就是错的。可是在 javascript 里面,
Person
和p1
都被认为是一个对象,而 没有 class 关键字这种声明的方式。那么,Person,p1 都是一个对象,所以可以定义新的属性比如Person.classname = "People";
和p1.name="Tom"
。这些在javascript里面都很正常。那么我们如何完成像 PHP里面那种类的定义呢,js给出的解决方式就是 把PHP里{public $name;fucntion say() { echo $this->name;}}
这一部分写到 Person 的一个特殊属性prototype
里面,例如:这样
p1
就有可以有p1.name
和p1.say()
了,可是 js里面存在的问题是这些属性是只读的而且是和其他其他该类的对象共享的(因为它其实是Person.prototype的属性,涉及原型链).如果你给p1.name = "John"
赋一个新的值,其实不会影响到Person.prototype.name
,而是给 p1 增加了一个新的属性 name所以在js里面一般是一个对象的方法和一些常量放在其类的定义之中,其他属性其实是动态添加的。所以 js 里面一般这样写:
至于第一个问题
isArray
放在哪里的问题,由于 Array 是一个对象,所以可以给其添加属性isArray
。这一点儿js跟其他语言不同,比如在PHP里面Person
不是一个变量名$Person->isPerson
是不合法的,当然如果是class Person { function isPerson(){}}
这样的代码有意义吗?所以说isPerson
这个函数其实不应该定义成类的方法(先不说静态方法),而是定义成独立于对象的全局方法。所以isPerson
应该是一个全局变量。但是全局变量么,你懂的,总是不好,给他选一个位置的话,在js里,那就放到Person
的属性里吧(其他语言里一般定义成类的静态方法或是放到其他的命名空间或是模块里),这样就不会污染全局变量空间。最后写个等价的 ES6 的 Person 定义可能更好理解;
答:因为成员方法的类型一般是引用类型function,引用类型一般占用空间大,所以我们一般提倡把成员方法写到原型上,因为当你new一个构造函数,生成的对象的从原型继承的属性都是指向同一个内存堆地址,从而达到节省内存的目的。
答:prototype的目的就是节省内存空间,为什么这么说?同理,当你new一个构造函数,生成的对象的从原型继承的属性都是指向同一个内存堆地址,从而达到节省内存的目的。如果不写到原型上的话,当你new一个构造函数,new一次,这个成员方法就会在生成在堆里面一次,浪费内存。从而你就明白了原型继承的好处。
修改父类prototype上的属性会影响到后面子类,前提是子类没有该自身属性。
可以理解为动态静态成员属性/方法 和 动态实例属性/方法
个人观点:
prototype
上定义的方法一般是这个类(js里用function模拟)的实例共用的方法,比如你说的Array.prototype.map
,因为任何一个数组都需要这个map功能,所以就给他定义在prototype
上,这样的话这个类的每个实例都可以共享这个方法。对于isArray
这样的方法,添加在prototype
上就不太合适了,否则调用起来也是a.isArray(a) //假设a是一个数组
。另外prototype
是函数的属性,你的p.prototype.name
的写法是不对的。另外使用和不使用prototype
的区别,就是会不会被继承吧。。。