- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
面向对象有一个特征是继承,即重用某个已有类的代码,在其基础上建立新的类,而无需重新编写对应的属性和方法,继承之后拿来即用; 。
在其他的面向对象编程语言比如Java中,通常是指,子类继承父类的属性和方法; 。
我们现在来看看,JS是如何实现继承这一个特征的; 。
要说明这个,我们首先要看看,每个对象都有的一个隐藏属性 [[Prototype]] ; 。
在JS中,每个对象 obj ,都有这样一个隐藏属性 [[Prototype]] ,它的值要么是null,要么是对另一个对象 anotherObj 的引用(不可以赋值为其他类型值),这另一个对象 anotherObj ,就叫做对象 obj 的原型; 。
通常说一个对象的原型,就是在说这个隐藏属性 [[Prototype]] ,也是在说它引用的那个对象,毕竟二者一致; 。
现在来创建一个非常简单的字面量对象,来查看一下这个属性:
可以看到,对象 obj 没有自己的属性和方法,但是它还有一个隐藏属性 [[Prototype]] ,数据类型是 Object ,说明它指向了一个对象(即原型),这个原型对象里面,有很多方法和一个属性; 。
其他的暂且不论,我们先重点看一下,红框的 constructor() 方法和 __proto__ 属性; 。
__proto__
) 从红框可以看到,属性 __proto__ 是一个访问器属性,有getter/setter特性(这个属性名前后各两个下划线); 。
问题是,它是用来访问哪个属性的?
我们来调用一下看看:
可以看到, __proto__ 访问器属性,访问的正是隐藏属性 [[Prototype]] ,或者说,它指向的正是原型对象; 。
值得一提的是,这是一个老式的访问原型对象的方法,现代编程语言建议使用 Object.getPrototypeOf/setPrototypeOf 来访问原型对象; 。
但是考虑兼容性,使用 __proto__ 也是可以的; 。
请注意, __proto__ 不能代表 [[Prototype]] 本身,它只是其一个访问器属性; 。
正因为它是访问器属性,也即具有getter和setter功能,我们现在可以控制对象的原型对象的指向了(并不建议这样做):
如上图,现在将其赋值为null,好了,现在 obj 对象没有原型了; 。
如上图,创建了两个对象,并且让 obj1 没有了原型,让 obj2 的原型是 obj1 ; 。
看看,此时 obj2.name 读取到 obj1 的属性 name 了,首先 obj2 在自身属性里找 name 没有找到,于是去原型上去找,于是找到了 obj1 的 name 属性了,换句话说, obj2 继承了 obj1 的属性了; 。
这就是JS实现继承的方式,通过原型这种机制; 。
让我们看看下面的代码:
正常的 obj2.name = 'Jerry' 的添加属性的语句,会成为 obj2 对象自己的属性,而不会去覆盖原型的同名属性,这是再正常不过了,继承得来的东西。只能读取,不能修改(访问器属性 __proto__ 除外); 。
现在的问题是,为什么 obj2.__proto__ 是 undefined ?上面不是刚刚赋值为 obj1 了吗?
原因就在于 __proto__ 是访问器属性,我们读取它实际上是在调用对应的getter/setter方法,而现在 obj2 的原型(即 obj1 )并没有对应的getter/setter方法,自然是 undefined 了; 。
现在综合一下,看下面代码:
为什么最后 obj2.__proto__ 输出的是 hello world ,为什么 __proto__ 成了 obj2 自己的属性了?
关键就在于红框的三句代码:
第一句 let obj2 = {} ,此时 obj2 有原型,有访问器属性 __proto__ ,一切正常; 。
第二句 obj2.__proto__ = obj1 ,这句调用 __proto__ 的setter方法,将 [[Prototype]] 的引用指向了 obj1 ; 。
这一句完成以后, obj2 因为 obj1 这个原型而没有访问器属性 __proto__ 了; 。
所以第三句 obj2.__proto__ = 'hello world' 的 __proto__ 已经不再是访问器属性了,而是一个普通的属性名了,所以这句就是一个普通的添加属性的语句了; 。
在隐藏属性 [[Prottotype]] 那里,看到其有一个 constructor() 方法,顾名思义,这就是构造器了; 。
在其他编程语言比如Java中,构造方法通常是和类名同名的函数,里面定义了对象的一些初始化代码; 。
当需要一个对象时,就通过 new 关键字去调用构造方法创建一个对象; 。
那在JS中,当我们 let obj = {} 去创建一个字面量对象的时候,发生了什么?
上面这句代码,其实就是 let obj = new Object() 的简写,也是通过 new 关键字去调用一个和类名同名的构造方法去创建一个对象,在这里就是构造方法 Object() ; 。
这种通过 new className() 调用构造方法创造的对象,称为类对象; 。
但是,再等一下,JS早期是没有类的概念的,那个时候大家又是怎么去创建对象的呢?
想一下,创建对象是不是需要一个构造方法(即一个函数),本质上是不是 new Function() 的形式去创建对象?
对咯,早期就是 new Function() 去创建对象的,这个 Function 就叫做构造函数; 。
这种通过 new Function() 调用构造函数创造的对象,称为函数对象; 。
构造函数和普通函数又有什么区别呢?除了要求是用 function 关键字声明的函数,并且命名建议大驼峰以外,几乎是没有区别的:
看,我们声明了一个构造函数 Cat() ,并通过 new Cat() 创造了一个对象 tom ; 。
打印 tom 发现,它有一个原型,这个原型和字面量对象的原型不一样,它有一个方法一个属性; 。
方法是 constructor() 构造器,指向的正是 Cat() 函数; 。
属性是另一个隐藏属性 [[Prototype]] ,暂时不去探究它是谁; 。
也就是说,函数对象的原型,是由另一个原型和 constructor() 方法组成的对象; 。
我们可以用代码来验证一下,类对象和函数对象的原型的异同点:
如上所示,创建了一个函数对象 tom 和一个类对象 obj ; 。
可以看出:
函数对象的原型的方法 constructor() 指向构造函数本身; 。
函数对象的原型的隐藏属性 [[Prototype]] 和字面量对象(Object对象)的隐藏属性,他们两的引用相同,指向的是同一个对象,暂时不去探究这个对象是什么,就认为它是字面量对象的原型即可; 。
还可以看到,无论是类对象,还是函数对象,其原型都有 constructor() 构造器; 。
这个构造器在创建对象的过程中,具体起了什么样的作用呢?
让我们先看看函数对象 tom 的这个原型是怎么来的?我们之前一直都是在说对象有一个隐藏属性 [[Prototype]] 指向原型对象,究竟是哪一步,让这个隐藏属性指向了原型对象呢?
事实上,每个函数都有一个属性 prototype ,默认情况下,这个属性 prototype 是一个对象,其中只含有一个方法 constructor ,而这个 constructor 指向函数本身(还有一个隐藏属性 [[Prototype]] ,指向字面量对象的原型); 。
可以用代码佐证,如下所示:
注意, prototype 要么是一个对象类型,要么是null,不可以是其他类型,这听起来很像隐藏属性 [[Prototype]] ,不过 prototype 只是函数的一个普通属性,对象是没有这个属性的; 。
来看下这个属性的特性吧:
可以看到,它不是一个访问器属性,只是一个普通属性,但是它不可配置不可枚举,只能修改值; 。
它的 value 值,眼熟吗?正是构造函数创建的函数对象的原型啊; 。
它居然还有一个特性 [[Prototype]] ,不要把它和 value 值里面的属性 [[Prototype]] 弄混,前者是 prototype 属性的特性,后者是 prototype 属性的一个隐藏属性,虽然此刻他们都指向字面量对象的原型,但是前者始终指向字面量对象的原型,后者则始终指向原型(而原型是会变的); 。
这里也不再去追究为什么它会有这样一个特性了,让我们把重点放在 prototype 属性本身; 。
事实上,只有在调用 new Function() 作为构造函数的时候,才会使用到这个 prototype 属性; 。
我们来仔细分析一下上面代码具体发生了什么:
let tom = new Cat() 这句代码的执行流程如下:
Cat.prototype
属性的特性 [[Prototype]]
(我们知道它指向字面量对象的原型)里面的 constructor()
构造器,创建一个字面量空对象,当然此时这个对象的隐藏属性 [[Prototype]]
也都已经存在了,将这个对象分配给 this
指针; this
指针给 tom
,即 tom
引用了这个字面量空对象,同时 this
指向了 tom
; Cat()
本身的语句,即 this.name = "Tom"
,于是 tom
就有了一个属性 name
; Cat.prototype
属性值 value
, 复制 (注意,这里是复制,不是赋值,这意味着这里不是传引用,而是传值)给 tom
的隐藏属性 [[Prototype]]
,即 tom.__proto__ = Cat.prototype
; 如果我们用代码去描述上面整个过程,就类似于下面这样:
// let tom = new Cat()的整个具体流程,类似于下面这样
let tom = {}; //创建字面量对象,并赋值给变量tom
tom.name = "Tom"; // 执行Cat()函数
tom.__proto__ = Cat.prototype; // 将Cat的prototype的属性值赋值给tom的隐藏属性[[Prototype]]
现在已经说清楚了 new Function() 发生的具体过程,上面代码的输出结果也佐证了我们所说的:
函数对象 tom 的原型正是 Cat 函数的属性 prototype 的值 value ,可以看到他们的 constructor() 构造器都指向 Cat 函数本身,并且 tom.name 的值 Tom ; 。
然后我们修改了 Cat 函数的 prototype 的值 value , Cat.prototype = Dog.prototype 语句将其设置成了 Dog 函数的 prototype 的值 value ; 。
让我们顺着刚刚说的流程,看看 let newTom = new Cat() 的执行过程:
newTom
; Cat()
函数本身,即 newTom.name = "Tom"
; newTom.__proto__ = Cat.prototype
,而 Cat.prototype = Dog.prototype
,所以 newTom.__proto__ = Dog.prototype
; 输出结果佐证了我们的执行过程,函数 newTom 的原型正是 Dog 函数的属性 prototype 的值 value ,他们的 constructor() 构造器都指向了 Dog 函数本身,但是 newTom.name 的值依然是"Tom"; 。
从上面前后两个输出结果也可以看出来,最后一步的 tom.__proto__ = Cat.prototype 确实是复制而不是赋值,否则在 Cat.prototype = Dog.prototype 语句之后, tom.__proto__ = Cat.prototype = Dog.prototype 了,但是输出结果表面并没有改变; 。
现在我们已经明白了函数对象的原型为什么是这个样子的,也明白了函数对象的 constructor() 构造器指向了构造函数本身; 。
现在让我们像下面这样,使用一下函数对象的 constructor() 构造器吧:
看上面的代码,我们现在已经知道 let tom = new Cat() 的时候都发生了什么,也知道此时 tom 的原型的 constructor() 构造器指向的是 Dog 函数; 。
所以 let spike = new tom.constructor() 这句代码,当 tom 去自己的属性里没有找到 constructor() 方法的时候,就去原型里面去找,于是找到了指向 Dog 函数的 constructor() 构造器,所以这句代码就等于 let spike = new Dog() ; 。
通过这段代码,好好体会一下函数对象的构造器吧.
其实从技术上来讲,构造函数和普通函数没有区别; 。
只是默认构造函数采用大驼峰命名法,并通过 new 操作符去创建一个函数对象; 。
new.target 。
我们怎样去判断一个函数的调用是普通调用,还是 new 操作符调用的呢?
如上所示,通过 new.target ,可以判断该函数是被普通调用的还是通过 new 关键字调用的; 。
构造函数的返回值 。
构造函数从技术上说,就是一个普通函数,所以当然也可能有 return 返回值(通常构造函数于情于理都是不会有 return 语句的); 。
之前说过 new Function() 的时候的具体流程,我们来看一下:
先创建一个字面量空对象; 。
将空对象赋值给 tom ; 。
执行 Cat() 函数,让 tom 有了属性 name ; 。
但是 Cat() 函数有 return 语句,返回了一个空对象 {} ,由 tom 接收了,也就是说 tom 被覆盖赋值了; 。
所以最后 tom 指向的是 return 语句的空对象,而不是最开始创建的空对象; 。
我们刚刚说了 new Function() 创建函数对象的时候,具体发生了什么,现在来看看创建类对象的时候,具体发生了什么; 。
以 Object 为例,因为它是一个类,是JS其他所有类的祖先,这一点与Java类似; 。
我们先看一下 Object 的 prototype 属性吧,是的,类和函数一样,也有这个属性(注意,是类有这个属性,而不是类的实例即对象有这个属性); 。
看上图,是不是很眼熟,这不就是字面量对象的原型吗?
是的,如上图所示,就是它; 。
还记得原型链吧,那么这个原型对象还有原型吗?
如上所示,没有了,指向null了,看样子我们已经走到了原型链的原点了,为了方便,我们就称呼 Object.prototype 为原始原型吧; 。
看看它的特性吧:
和函数的 prototype 属性的特性,如出一辙,但是注意,它的 writable 属性是 false 了,这意味着我们再也无法对这个属性做任何操作了; 。
这是当然,它可是所有类的祖先,怎么能随意更改呢; 。
这下我们就能明白 new ClassName() 的时候大概流程是什么样子了; 。
以 let obj = {} 为例(其实就是 let obj = new Object() ):
Objecet.prototype
属性的特性 [[Prototype]]
里面的 constructor()
构造器(不再继续深究这个构造器了),创建一个字面量空对象,当然此时这个对象的隐藏属性 [[Prototype]]
也都已经存在了; obj
,即 obj
引用了这对象,同时 this
指针也就指向了 obj
; Object()
本身的语句,就不再进一步去研究这个构造方法了,总之此时 obj
已经是一个有着很多内置方法的字面量对象了; Object.prototype
属性值 value
,复制给 obj
的隐藏属性 [[Prototype]]
,即 obj.__proto__ = Object.prototype
; 注意,其实流程不完全是上面这样子,与构造函数的流程还有一点点区别,主要是第三步,还有一个构造器的执行,这和类的继承有关系,详细的在后面 new className()的时候发生了什么 里面具体说明; 。
我们刚刚说了, Object.prototype 属性的所有特性都是 false ,意味着我们对这个属性无法再做任何操作了; 。
这只是再说,我们不能对其本身做任何删改的操作了,但是它本身依然是一个对象,这意味着我们可以正常的向其添加属性和方法; 。
如上图所示,我们向 Object.prototype 属性对象里添加了 hello() 方法,并且由 obj 对象通过原型调用了这个方法; 。
我们已经了解了函数对象的原型,和原始原型,再来看看类对象的原型; 。
我们把这三种放一起做个比较吧:
我们自定义了类 classA ,自定义了函数 functionA ,并创建了类对象 clsA 和函数对象 funcA ,以及字面量对象; 。
可以看出,类对象与函数对象的原型的形式,是一致的,只是各自原型里的 constructor() 指向各自的类/函数,即红框部分不同; 。
而他们的原型的原型则是一致的,和字面量对象的原型一样,都指向了原始原型,即绿框部分相同; 。
上面的输出结果佐证了这一点; 。
从这也可以看出来,其他类都是继承自原始类 Object 的,只是原型链的长短罢了,最终都可以溯源到原始类 Object ; 。
很显然,类与构造函数,很类似; 。
尽管类对象和函数对象有相似的原型,但是不代表类与构造函数就完全一样了,他们之间的区别还是很大的:
类型不同,定义形式不同 。
类名后不需要括号,构造函数名后需要加括号; 。
类的方法声明形式和构造函数的方法不一样; 。
打印类和构造函数,类前的类型是 class ,构造函数前的类型是 f ,即 function ; 。
注意,不能使用 typeof 操作符,它会认为类和构造函数都是 function 。
prototype不一样 。
如上所示,类的方法,会成为 prototype 的方法,但是构造函数的方法不会成为 prototype 的方法; 。
也即构造函数的 prototype 始终由 constructor() 和原始原型组成,函数对象无法通过原型去调用在构造函数里定义的方法; 。
函数对象如果想要调用 method1() 方法,就不能写成 let method1 = function(){} ,而是 this.method1 = function(){} ,将其变为函数对象自己的方法; 。
prototype的特性不一样 。
类的 prototype 是不可写的,但是构造函数的 prototype 是可写的; 。
方法的特性不一样 。
由于函数对象不能通过原型继承方法,这里只展示类的方法的特性,如上所示,类的方法,是不可枚举的,也即不会被 for-in 语法遍历到; 。
模式不同 。
由于类是后来才有的概念,所以类总是使用严格模式,即不需要显示使用 use strict ,类总是在严格模式下执行; 。
而构造函数则不同,默认是普通模式,需要显示使用 use strict 才会在严格模式下执行; 。
[[IsClassConstructor]] 。
类有隐藏属性 [[IsClassConstructor]] ,其值为true; 。
这要求必须使用 new 关键字去调用它,像普通函数一样调用会出错:
但是很显然,构造函数本身就是一个函数,是可以像普通函数一样去调用的; 。
构造器 constructor 。
由于函数对象不能通过原型继承方法,所以无法自定义构造器; 。
但是类对象可以继承啊,所以可以自定义构造器并在 new 的时候调用; 。
从图上可以看出,我们是无法去自定义构造函数的构造器的,它依然还是按照我们所说的流程去创建函数对象的; 。
我们现在看看,类自定义构造器,是怎么按照我们的流程去创建类对象的:
先调用 classA.prototype 的特性 [[Prototype]] 里的构造器去创建一个字面量空对象; 。
将空对象赋值给变量 clsA ; 。
然后执行构造方法 classA() 本身的语句; 。
首先添加了属性 outterName ; 。
然后又遇到了 constructor() 方法(注意该构造器与 classA.prototype.constructor 不是同一个东西),于是又执行了这个构造器的语句,添加了属性 innerName ; 。
由此我们可以得出,类在创建类对象的时候,流程依然是我们所述的流程; 。
但是在遇到类里面的同名方法 constructor() 时候,不会将其作为原型方法,而是会立即运行该构造器; 。
另外,像 outterName 这样的属性,不会成为 prototype 的属性,也就是说,类只有定义的方法(除了 constructor 构造器)会进入 prototype 的属性,成为原型被继承; 。
上面刚刚描述了类自定义构造器之后,创建对象是一个什么样的流程; 。
现在来仔细理解一下类的构造器,事实上,如果我们不显式自定义构造器,类也会默认提供一个下面这样的构造器:
constructor() {
super();
}
这里的 super() 实际上就是在调用其父类的构造方法(注意不是指父类的构造器 constructor() ,而是指父类自身); 。
用代码来验证一下吧:
我们先来看一下 let c = new classC() 的时候,具体流程是什么样的吧:
classC.prototype
属性的特性 [[Prototype]]
(它总是指向原始原型),创建一个字面量空对象; c
; classC()
的语句,通常会有添加对象的属性和方法的语句,这里没有; constructor()
构造器(如果没有就提供一个默认的构造器),这里有,于是立即执行这个构造器;
super()
,实际上就是执行构造函数 classA()
的语句,于是添加了属性 nameA
; this.nameB = 'C'
,于是添加了属性 nameC
; classC.prototype
的 value
值,复制给 c
的隐藏属性 [[Prototype]]
,即 c.__proto__ = classC.prototype
; 整个完整流程如上所示; 。
现在来试着对着流程看看 let b = new classB() 吧:
b
; classB()
的语句,添加了属性 nameB
; super()
即执行 classA()
的语句,于是添加了属性 nameA
; b
的原型为 classB.prototype
的 value
值; 输出结果也验证了我们所说的; 。
之前已经说过,通过 __proto__ 属性去操作原型的方法,是历史的过时的方法,实际上并不推荐; 。
现代JS有以下方法,供我们去操作原型:
Object.getPrototypeOf(obj) 。
此方法,返回对象 obj 的隐藏属性 [[Prototype]] ; 。
Object.setPrototypeOf(obj, proto) 。
此方法,将对象 obj 的隐藏属性 [[Prototype]] 指向新的对象 proto ; 。
Object.create(proto, descriptors) 。
此方法,创建一个空对象,并将其隐藏属性 [[Prototype]] 指向 proto ; 。
同时,可选参数 descriptors 可以给空对象添加属性,如下所示:
现在应该已经理解了原型是一个什么样的概念,以及如何去访问原型; 。
正如继承有儿子继承父亲,父亲继承爷爷一样,有这样一个往上溯源的关系,原型也可以这样往上溯源,这就是原型链的概念; 。
用代码去理解一下吧:
我们定义了三个对象A/B/C,并且设置C的原型是B,B的原型是A; 。
读取 C.nameA 的时候,首先在C自己的属性里去找,没有找到; 。
于是去原型B的属性里去找,没有找到; 。
再去B的原型A的属性里去找,找到并输出; 。
可以看C展开的一层层结构,可以很清晰的看到原型链的存在; 。
由此也可以看出,JS是单继承的,同Java一致; 。
但是正常的继承,肯定不是这样手动去设置对象的原型的,而是自动去设置的; 。
在JS中,继承的关键字也是 extends ,也是描述类的父子关系的; 。
上面代码, classC 继承 classB ,而 classB 继承 classA ; 。
所以 classC 的对象,继承了他们的属性,便有了三个属性 nameA/nameB/nameC ,这也说明,属性是不放在原型里的,而是会在创建对象的时候,直接成为 classC 的属性; 。
classC 的原型,有一个属性一个方法,方法是 constructor() 构造器指向自己,属性是另一个原型; 。
注意,打印出来的原型后面标注的 classX ,原型指的是对象,不是类,所以 classC 的原型不是指 classB 这个类本身,而是指其来源于 classB ; 。
紫色框:对象 c 的原型,即 c.__proto__ == classC.prototype ; 。
橘色框: classB.prototype ,即对象 c 的原型的原型 c.__proto__.__proto__ == classB.prototype ; 。
绿色框: classA.prototype ,即对象 c 的原型的原型的原型 c.__proto__.__proto__.__proto__ == classA.prototype ; 。
红色框: Object.prototype ,也即原始原型 c.__proto__.__proto__.__proto__.__proto__ == Object.prototype ; 。
这是一条完整的原型链,从中也能看出继承是什么样的一个形式; 。
最后此篇关于JavaScript:原型(prototype)的文章就讲到这里了,如果你想了解更多关于JavaScript:原型(prototype)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
好吧,我怀疑这是一个独特的情况,所以要么有人这样做了,要么有人认为这是不可能的,至少以我所要求的方式。 我有 2 个原型(prototype)变量(函数),一个是父变量,另一个是助手。我想做的是从助手
这是 JavaScript 大师的问题。我正在尝试更优雅地使用 JavaScript 原型(prototype)模型。这是我的实用程序代码(它提供了真实的原型(prototype)链并正确使用 ins
我们知道在 JavaScript 中有一个用于数组的 .forEach() 方法。但是字符串没有内置该方法。 那么,下面的代码片段有没有问题:String.prototype.forEach = Ar
我们知道在 JavaScript 中有一个用于数组的 .forEach() 方法。但是字符串没有内置该方法。 那么,下面的代码片段有没有问题:String.prototype.forEach = Ar
我看到了两种不同的模式和解释。来自 DailyJS 和许多其他人的一篇:矩形.prototype = new Shape(); 然后是 Crockford 的 here 这意味着只是 矩形.proto
尝试在 Object.prototype 以及 String.prototype 和 Number.prototype 上定义一个 hashCode 方法>。我正在使用以下方法定义原型(prototy
在本教程中,您将借助示例了解 JavaScript 中的原型。 在学习原型之前,请务必查看以下教程: JavaScript 对象 JavaScript 构造函数 如您所知,您可以使用对象构造函
当构造新对象时,该对象被设置为委托(delegate)任何尚未显式设置为其构造函数原型(prototype)的属性。这意味着我们可以稍后更改原型(prototype),并且仍然可以看到实例中的更改。
我正在努力获得更好的 JavaScript 实用知识。所以,我买了 Douglas Crockford 的书“JavaScript the good parts”。 我现在很难掌握原型(prototy
我的理解是相同类型的所有对象将共享相同的原型(prototype)。因此对原型(prototype)的更改将反射(reflect)在每个对象上。但是值类型的属性似乎不是这样。这种属性是如何存储的? f
这个问题在这里已经有了答案: 关闭 12 年前。 Possible Duplicate: JavaScript: Class.method vs. Class.prototype.method 创建
为什么在 MDN 函数中 polyfills 使用“if (!Array.prototype.filter)”? if (!Array.prototype.filter) { Array.prot
这个问题已经有答案了: Assigning prototype methods *inside* the constructor function - why not? (6 个回答) 已关闭 7 年
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭 9 年前。 Improve this qu
面向对象有一个特征是继承,即重用某个已有类的代码,在其基础上建立新的类,而无需重新编写对应的属性和方法,继承之后拿来即用; 。 在其他的面向对象编程语言比如Java中,通常是指,子类继承父类的属性和
OOP 中原型(prototype)设计模式最重要的部分之一是我们不会从头开始创建新对象,我们只是使用 clone() 函数从现有对象克隆它们。 那么clone()函数是深拷贝还是浅拷贝? 如果它是一
在进行原型(prototype)设计时,您在多大程度上放弃了最佳实践来支持代码和修复黑客攻击?当然,代码并不打算在完整的生产环境中保留。 补充:我正在研究一个用 Python 制作的相当大的半工作原型
我开始学习设计模式。我知道原型(prototype)是用来制作我已经拥有的对象的精确副本,而享元是用来制作类似的对象。 我已经编写了 2D 平台游戏,例如马里奥(Java)。有很多相同的敌人,唯一的区
我正在使用 Maven 生成原型(prototype)。我能够使原型(prototype)生成正常,并且它生成的项目模板按预期工作。唯一的问题是在我的 shell 脚本中。脚本中注释掉的任何内容都会被
我想用 primefaces 配置一个 Java EE 项目。我在某处读到可以使用 mvn arechetype:generate 创建项目结构。当我使用它时,我只看到了 41 个选项,而在该教程中,
我是一名优秀的程序员,十分优秀!