{"action":"created","issue":{"url":"https://api.github.com/repos/KieSun/Blog/issues/2","repository_url":"https://api.github.com/repos/KieSun/Blog","labels_url":"https://api.github.com/repos/KieSun/Blog/issues/2/labels{/name}","comments_url":"https://api.github.com/repos/KieSun/Blog/issues/2/comments","events_url":"https://api.github.com/repos/KieSun/Blog/issues/2/events","html_url":"https://github.com/KieSun/Blog/issues/2","id":305009482,"node_id":"MDU6SXNzdWUzMDUwMDk0ODI=","number":2,"title":"深度解析原型中的各个难点","user":{"login":"KieSun","id":11811888,"node_id":"MDQ6VXNlcjExODExODg4","avatar_url":"https://avatars1.githubusercontent.com/u/11811888?v=4","gravatar_id":"","url":"https://api.github.com/users/KieSun","html_url":"https://github.com/KieSun","followers_url":"https://api.github.com/users/KieSun/followers","following_url":"https://api.github.com/users/KieSun/following{/other_user}","gists_url":"https://api.github.com/users/KieSun/gists{/gist_id}","starred_url":"https://api.github.com/users/KieSun/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/KieSun/subscriptions","organizations_url":"https://api.github.com/users/KieSun/orgs","repos_url":"https://api.github.com/users/KieSun/repos","events_url":"https://api.github.com/users/KieSun/events{/privacy}","received_events_url":"https://api.github.com/users/KieSun/received_events","type":"User","site_admin":false},"labels":[{"id":866488741,"node_id":"MDU6TGFiZWw4NjY0ODg3NDE=","url":"https://api.github.com/repos/KieSun/Blog/labels/%E5%8E%9F%E5%9E%8B","name":"原型","color":"bfdadc","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":6,"created_at":"2018-03-14T03:19:54Z","updated_at":"2018-07-17T13:12:14Z","closed_at":null,"author_association":"OWNER","body":"本文不会过多介绍基础知识，而是把重点放在原型的各个难点上。\r\n\r\n\r\n\r\n![](https://user-gold-cdn.xitu.io/2018/3/13/1621e8a9bcb0872d?w=488&h=590&f=png&s=151722)\r\n\r\n\r\n\r\n大家可以先仔细分析下该图，然后让我们进入主题\r\n\r\n\r\n\r\n## `prototype`\r\n\r\n首先来介绍下 `prototype` 属性。这是一个显式原型属性，只有函数才拥有该属性。基本上所有函数都有这个属性，但是也有一个例外\r\n\r\n```js\r\nlet fun = Function.prototype.bind()\r\n```\r\n\r\n如果你以上述方法创建一个函数，那么可以发现这个函数是不具有 `prototype `  属性的。\r\n\r\n#### prototype 如何产生的\r\n\r\n当我们声明一个函数时，这个属性就被自动创建了。\r\n\r\n```js\r\nfunction Foo() {}\r\n```\r\n\r\n并且这个属性的值是一个对象（也就是原型），只有一个属性 `constructor` \r\n\r\n![](https://user-gold-cdn.xitu.io/2018/3/13/1621ea3db70ced0a?w=304&h=86&f=png&s=11191)\r\n\r\n `constructor` 对应着构造函数，也就是 `Foo`。\r\n\r\n\r\n\r\n#### constructor\r\n\r\n `constructor` 是一个公有且不可枚举的属性。一旦我们改变了函数的 `prototype` ，那么新对象就没有这个属性了（当然可以通过原型链取到 `constructor`）。\r\n\r\n![](https://user-gold-cdn.xitu.io/2018/3/13/1621eaa750c877ae?w=408&h=204&f=png&s=27310)\r\n\r\n那么你肯定也有一个疑问，这个属性到底有什么用呢？其实这个属性可以说是一个历史遗留问题，在大部分情况下是没用的，在我的理解里，我认为他有两个作用：\r\n\r\n- 让实例对象知道是什么函数构造了它\r\n- 如果想给某些类库中的构造函数增加一些自定义的方法，就可以通过 `xx.constructor.method` 来扩展\r\n\r\n## `_proto_`\r\n\r\n这是每个对象都有的隐式原型属性，指向了创建该对象的构造函数的原型。其实这个属性指向了 [[prototype]]，但是 [[prototype]] 是内部属性，我们并不能访问到，所以使用 `_proto_` 来访问。\r\n\r\n因为在 JS 中是没有类的概念的，为了实现类似继承的方式，通过 `_proto_ ` 将对象和原型联系起来组成原型链，得以让对象可以访问到不属于自己的属性。\r\n\r\n\r\n\r\n#### 实例对象的 `_proto_` 如何产生的\r\n\r\n从上图可知，当我们使用 `new` 操作符时，生成的实例对象拥有了 `_proto_`属性。\r\n\r\n```js\r\nfunction Foo() {}\r\n// 这个函数是 Function 的实例对象\r\n// function 就是一个语法糖\r\n// 内部调用了 new Function(...)\r\n```\r\n\r\n所以可以说，在 `new` 的过程中，新对象被添加了 `_proto_` 并且链接到构造函数的原型上。\r\n\r\n#### new 的过程\r\n\r\n1. 新生成了一个对象\r\n2. 链接到原型\r\n3. 绑定 this\r\n4. 返回新对象\r\n\r\n在调用 `new` 的过程中会发生以上四件事情，我们也可以试着来自己实现一个 `new`\r\n\r\n```js\r\nfunction create() {\r\n    // 创建一个空的对象\r\n    let obj = new Object()\r\n    // 获得构造函数\r\n    let Con = [].shift.call(arguments)\r\n    // 链接到原型\r\n\tobj.__proto__ = Con.prototype\r\n    // 绑定 this，执行构造函数\r\n    let result = Con.apply(obj, arguments)\r\n    // 确保 new 出来的是个对象\r\n    return typeof result === 'object' ? result : obj\r\n}\r\n```\r\n\r\n对于实例对象来说，都是通过 `new` 产生的，无论是 `function Foo()` 还是 `let a = { b : 1 }` 。\r\n\r\n对于创建一个对象来说，更推荐使用字面量的方式创建对象。因为你使用 `new Object()` 的方式创建对象需要通过作用域链一层层找到 `Object`，但是你使用字面量的方式就没这个问题。\r\n\r\n```js\r\nfunction Foo() {}\r\n// function 就是个语法糖\r\n// 内部等同于 new Function()\r\nlet a = { b: 1 }\r\n// 这个字面量内部也是使用了 new Object()\r\n```\r\n\r\n\r\n\r\n## Function.__proto__ === Function.prototype\r\n\r\n对于对象来说，`xx.__proto__.contrcutor` 是该对象的构造函数，但是在图中我们可以发现 `Function.__proto__ === Function.prototype`，难道这代表着 `Function` 自己产生了自己?\r\n\r\n答案肯定是否认的，要说明这个问题我们先从 `Object` 说起。\r\n\r\n从图中我们可以发现，所有对象都可以通过原型链最终找到 `Object.prototype` ，虽然 `Object.prototype` 也是一个对象，但是这个对象却不是 `Object` 创造的，而是引擎自己创建了 `Object.prototype` 。**所以可以这样说，所有实例都是对象，但是对象不一定都是实例。**\r\n\r\n接下来我们来看 `Function.prototype` 这个特殊的对象，如果你在浏览器将这个对象打印出来，会发现这个对象其实是一个函数。\r\n\r\n![](https://user-gold-cdn.xitu.io/2018/3/14/162224517fbd40dc?w=494&h=116&f=png&s=18634)\r\n\r\n我们知道函数都是通过 `new Function()` 生成的，难道 `Function.prototype` 也是通过 `new Function()` 产生的吗？答案也是否定的，这个函数也是引擎自己创建的。首先引擎创建了 `Object.prototype` ，然后创建了 `Function.prototype` ，并且通过 `__proto__` 将两者联系了起来。这里也很好的解释了上面的一个问题，为什么 `let fun = Function.prototype.bind()` 没有 `prototype` 属性。因为 `Function.prototype` 是引擎创建出来的对象，引擎认为不需要给这个对象添加 `prototype` 属性。\r\n\r\n**所以我们又可以得出一个结论，不是所有函数都是 `new Function()` 产生的。**\r\n\r\n有了 `Function.prototype` 以后才有了 `function Function()` ，然后其他的构造函数都是 `function Function()` 生成的。\r\n\r\n现在可以来解释 `Function.__proto__ === Function.prototype` 这个问题了。因为先有的 `Function.prototype` 以后才有的 `function Function()` ，所以也就不存在鸡生蛋蛋生鸡的悖论问题了。对于为什么 `Function.__proto__` 会等于 `Function.prototype` ，个人的理解是：其他所有的构造函数都可以通过原型链找到 `Function.prototype` ，并且 `function Function()` 本质也是一个函数，为了不产生混乱就将 `function Function()` 的 `__proto__` 联系到了 `Function.prototype` 上。\r\n\r\n## 总结\r\n\r\n- `Object` 是所有对象的爸爸，所有对象都可以通过 `__proto__` 找到它\r\n- `Function` 是所有函数的爸爸，所有函数都可以通过 `__proto__` 找到它\r\n- `Function.prototype` 和 `Object.prototype` 是两个特殊的对象，他们由引擎来创建\r\n- 除了以上两个特殊对象，其他对象都是通过构造器 `new` 出来的\r\n- 函数的 `prototype` 是一个对象，也就是原型\r\n- 对象的 `__proto__` 指向原型， `__proto__` 将对象和原型连接起来组成了原型链"},"comment":{"url":"https://api.github.com/repos/KieSun/Blog/issues/comments/405576562","html_url":"https://github.com/KieSun/Blog/issues/2#issuecomment-405576562","issue_url":"https://api.github.com/repos/KieSun/Blog/issues/2","id":405576562,"node_id":"MDEyOklzc3VlQ29tbWVudDQwNTU3NjU2Mg==","user":{"login":"Daoma666","id":34900760,"node_id":"MDQ6VXNlcjM0OTAwNzYw","avatar_url":"https://avatars3.githubusercontent.com/u/34900760?v=4","gravatar_id":"","url":"https://api.github.com/users/Daoma666","html_url":"https://github.com/Daoma666","followers_url":"https://api.github.com/users/Daoma666/followers","following_url":"https://api.github.com/users/Daoma666/following{/other_user}","gists_url":"https://api.github.com/users/Daoma666/gists{/gist_id}","starred_url":"https://api.github.com/users/Daoma666/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Daoma666/subscriptions","organizations_url":"https://api.github.com/users/Daoma666/orgs","repos_url":"https://api.github.com/users/Daoma666/repos","events_url":"https://api.github.com/users/Daoma666/events{/privacy}","received_events_url":"https://api.github.com/users/Daoma666/received_events","type":"User","site_admin":false},"created_at":"2018-07-17T13:12:14Z","updated_at":"2018-07-17T13:12:14Z","author_association":"NONE","body":"@KieSun 可能是，但我也不知道什么原因，从掘金过来的，两个网站上图片都显示一半，但网页已经加载完了。。。"}}
{"actor":{"display_login":"Daoma666"}}