前言
没有引用任何文件正文
/**
* Creates an object that inherits from the `prototype` object. If a
* `properties` object is given, its own enumerable string keyed properties
* are assigned to the created object.
*
* @since 2.3.0
* @category Object
* @param {Object} prototype The object to inherit from.
* @param {Object} [properties] The properties to assign to the object.
* @returns {Object} Returns the new object.
* @example
*
* function Shape() {
* this.x = 0
* this.y = 0
* }
*
* function Circle() {
* Shape.call(this)
* }
*
* Circle.prototype = create(Shape.prototype, {
* 'constructor': Circle
* })
*
* const circle = new Circle
* circle instanceof Circle
* // => true
*
* circle instanceof Shape
* // => true
*/
function create(prototype, properties) {
prototype = prototype === null ? null : Object(prototype)
const result = Object.create(prototype)
return properties == null ? result : Object.assign(result, properties)
}
export default create
这个函数的作用是创建某个原型对象的新对象,可以传入可枚举的配置属性对新生成的对象进行属性的配置修改,主要使用到的还是Object.create()函数。虽然这个函数代码简短,但是设计到原型、及其基于原型的继承和对象属性拷贝,涉及的知识点还是比较多的。下面我就简单的分析一下包含的知识点然后再回来看这段代码及其实例吧。
1.原型
我们都知道javascript是基于原型的一门语言。原型是javascript中比较重要也是比较难的一点。初学的时候很容易被卷入鸡生蛋,蛋生鸡的死胡同里。不信,我们来试试几个题:
Function.prototype === Function.__proto__ //true
Function.prototype === Object.__proto__ //true
Function.prototype.__proto__ === Object.prototype //true
Object.prototype === Object.__proto__ //false
Function instanceof Function //true
Object instanceof Object //true
Object instanceof Function //true
Function instanceof Object //true
Object.__proto__ instanceof Object //true
Object.prototype instanceof Object //false
Function.prototype instanceof Object //true
Function.__proto__ instanceof Object //true
先有Function还是先有Object构造函数? //没有先后顺序,如果有那也是作者编写代码时的先后
/*所有对象由构造函数生成,而构造函数也是对象,那这个构造函数对象是怎么生成的呢?如果把对象比作鸡,构造函数比作蛋,那就是鸡生蛋蛋生鸡的悖论*/
/*归根到底就是
Function构造函数本身也算是Function类型的实例吗?
Function构造函数的prototype属性和__proto__属性都指向同一个原型,是否可以说Function对象是由Function构造函数创建的一个实例?*/
/*这一悖论的根源来自Object和Function互为对方的实例*/
如果对上述问题你都知道,并且对其有较深的理解,那么你可能已经理解了什么是原型。当然如果你对这几道题还有疑惑,那么你就需要更加深入的理解这一知识点。
好了,话说回来,我来简单的复习一下原型的知识点和应当注意的地方,并且解决掉鸡生蛋蛋生鸡的问题。
根据js红宝书讲的,每一个构造函数A创建的时候都会创建另外一个对象,这个对象为A.prototype,(我们知道函数也是一个对象,所以以后就叫函数为函数对象,还要注意不是所有函数都有prototype属性,比如
Object.prototype.toString.bind(Array)),此时该函数对象存在一个属性叫prototype,其指向为这个自动创建的对象(原型对象)。并且使用该构造函数创建实例时该实例a会存在__proto__属性(其实是[[Prototype]]属性,只不过Firefox和Chrome提供了"__proto__"这个非标准的访问器)来指向该原型对象A.prototype。所以我们可以知道当创建一个构造函数和实例化其构造函数生成的实例以及自动创建的对象(原型对象)关系如下:这里我们可以提出几个疑问了?
- 原型对象是怎么创建的或者说是通过什么方式创建的,new 还是 对象字面量?有没有构造函数?如果有,构造函数是什么?
- 构造函数对象又是怎么创建的?有没有构造函数?如果有,构造函数是什么?
- 所有对象都是构造函数的实例吗?或者所有对象都是Object的实例吗?
- 鸡生蛋蛋生鸡问题
要解决这几个问题我们需要了解几个重要概念(哲学三问~~~~)
1.什么是对象,什么是实例对象
An object is a collection of properties and has a single prototype object. The prototype may be the null value.
所有的具有属性的数据集合就是对象。而实例对象是由构造函数创建的一个具象化物品,所有的实例对象都是对象,而不是所有对象都是实例对象。比如最常见的Object.prototype就是内置对象,并不是任何构造函数的实例。当然我们遇到的大多数都是实例对象。
2.什么是函数
ECMAScript规范定义的函数:
对象类型的成员,标准内置构造器Function的一个实例,并且可做为子程序被调用。
注: 函数除了拥有命名的属性,还包含可执行代码、状态,用来确定被调用时的行为。函数的代码不限于 ECMAScript。
函数就是构造函数Function的实例。那什么是构造函数呢?ECMAScript规范如此定义:
创建和初始化对象的函数对象
注:构造器的“prototype”属性值是一个原型对象,它用来实现继承和共享属性。
构造函数对象作为一个函数对象均实例化自构造函数Function(构造函数对象是实例化对象的一部分),当然构造函数Function也是函数对象,也实例化自Function(这里你需要明白实例化是将原型对象的属性拷贝到实例化对象中,而Function的原型Function.prototype是个内置对象)。所以如果不考虑继承的话,构造函数对象都是基于Function.prototype这个内置对象的。
注:对于Function是否是Function构造函数实例存在一定的争议
3.什么是原型
ECMAScript标准如下:
为其他对象提供共享属性的对象。
当构造器创建一个对象,为了解决对象的属性引用,该对象会隐式引用构造器的“prototype”属性。通过程序表达式 constructor.prototype 可以引用到构造器的“prototype”属性,并且添加到对象原型里的属性,会通过继承与所有共享此原型的对象共享。另外,可使用 Object.create 内置函数,通过明确指定原型来创建一个新对象。
原型就是为对象提供共享属性的对象,不然每个对象的属性都是私有的,导致空间浪费之类的问题。其创建是在创建构造函数的时候隐式创建的。其属性constructor指向构造函数,而构造函数的prototype属性指向原型对象。
4.我们说一个对象是某个构造函数实例的根据是啥
我们一般通过instanceof 判断对象是否是构造函数的实例,而instanceof 实际是调用hasInstance,而hasInstance的循环调用prototype直到最顶上那个对象,如果这个与第二个参数的prototype一致,那就是真,否则假。也就是遍历实例对象的原型链并判断构造函数的原型是否在该原型链上。是Object.getPrototypeOf(Function) === Function.prototype 或者简单来说Object.__proto__ === Function.prototype的结果,只是一种运算关系,满足这种关系就判定是该构造函数的实例。
好了回到正题,我们一一解决上述问题:
1.原型对象通过调用Object.create()来创建,是Object的实例。可以通过A.prototype.__proto__ === Object.prototype来说明
2.构造函数对象通过构造函数的构造函数Function来创建,构造函数对象是Function的实例。由于构造函数Function也是对象,所以Function既是鸡也是蛋。具体的请看4.
3.什么是对象那里已经回答了,不是所有对象都是Object的实例
4.要解决鸡生蛋蛋生鸡问题,不能从javascript的角度上思考,必须跳出来从编译角度上看这一问题。javascript的实现不是javascript,而是更为底层的语言,比如c,c++之类的。我们可以很轻易的知道Function这个函数对象是内置对象(不晓得是通过啥语言创建了这个对象),只不过其属性prototype和__proto__都指向另外一个内置对象Function.prototype,所以从我们javascript语法的角度就会得到悖论,但实际上是该对象先存在,只不过表现为这种关系罢了。(感觉只要说不过去扯上内置对象都可以说过去啊23333)
扯不下去了。下面贴一个对原型解释很好的图:
给个别人的解释:
JavaScript引擎是个工厂。
最初,工厂做了一个最原始的产品原型。
这个原型叫Object.prototype,本质上就是一组无序key-value存储({})
之后,工厂在Object.prototype的基础上,研发出了可以保存一段“指令”并“生产产品”的原型产品,叫函数。
起名为Function.prototype,本质上就是[Function: Empty](空函数)
为了规模化生产,工厂在函数的基础上,生产出了两个构造器:
生产函数的构造器叫Function,生产kv存储的构造器叫Object。
你在工厂定制了一个产品,工厂根据Object.prototype给你做了一个Foo.prototype。
然后工厂发现你定制的产品很不错。就在Function.prototype的基础上做了一个Foo的构造器,叫Foo。
工厂在每个产品上打了个标签__proto__,以标明这个产品是从哪个原型生产的。
为原型打了个标签constructor,标明哪个构造器可以依照这个原型生产产品。
为构造器打了标签prototype,标明这个构造器可以从哪个原型生产产品。
所以,我觉得先有Function还是Object,就看工厂先造谁了。其实先做哪个都无所谓。因为在你定制之前,他们都做好了。
再给个比较好的链接:
2.原型链和继承
原型链就是如果一个实例对象a的原型对象A.prototype为另外一个实例对象b的话,a就可以共享a.__proto__,a.__proto__.__proto__(b.__proto__)上的属性,这个原型之间链状的东西我们就叫他原型链。其主要作用是用于继承。
继承是为了解决new的实例对象不能共享属性和方法的缺点。不说啥了,上六种继承方式:
只给链接,不想写了。。。。。。有空的时候再修改和放图吧。。。
源码分析
1.Object()方法描述:
- obj if obj is an object
- {} if obj is undefined or null
这玩意儿和这个语句差不多:obj = obj || {},但是区别在于如果传入的obj是原始值,比如4,这个方法会返回包装过的Number对象而不是4这个原始值。
2.Object.create(prop):根据传入的对象创建一个将该对象作为原型的对象,执行空的构造函数。
Object.create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
Object.create是内部定义一个对象,并且让F.prototype对象 赋值为引进的对象/函数 o,并return出一个新的对象。
new做法是新建一个实例对象o,并且让o的__proto__指向了原型对象。并且使用call 进行强转作用环境。从而实现了实例的创建。
偷偷截张图:
3.Object.assign():主要用于对象属性合并和添加。没什么好说的。注意的是其方式是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。如果要实现深拷贝需要递归的进行属性拷贝。而且这个方法可以被...代替使用
使用方式
function Shape() {
this.x = 0
this.y = 0
}
function Circle() {
Shape.call(this)
}
Circle.prototype = create(Shape.prototype, {
'constructor': Circle
})
const circle = new Circle
circle instanceof Circle
// => true
circle instanceof Shape
// => true和Object.create(proto[, propertiesObject])没啥区别。用于实现类式继承
使用场景
用于寄生组合式继承避免多次调用构造函数。其他的。。。。没时间了,,
结语
两天赶完了,由于时间跨度原因,估计有些地方牛头不对马嘴。mayiga。。。