https://github.com/a8397550/react-source-share.git

一 Symbol的使用

Symbol的使用
React源码中大量的使用Symbol来作为标示符,标识一些内容在无法使用Symbol时会使用32位Number作为标识符。

Iterator的使用,迭代器,每个实现了迭代器的对象都可以使用next()方法
image.png

  1. /**
  2. * @description React的版本号
  3. */
  4. var ReactVersion = '16.9.0';
  5. /**
  6. * @description 检测是否支持Symbol用的
  7. */
  8. var hasSymbol = typeof Symbol === 'function' && Symbol.for;
  9. /**
  10. * @description Symbol.for('react.element')
  11. */
  12. var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7;
  13. /**
  14. * @description Symbol.for('react.portal')
  15. */
  16. var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca;
  17. /**
  18. * @description Symbol.for('react.fragment') 分组用的标识,React.Fragment允许将子列表分组,而无需向 DOM 添加额外节点
  19. */
  20. var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb;
  21. /**
  22. * @description 不知道这个是干什么的
  23. */
  24. var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc;
  25. /**
  26. * @description 不知道这个是干什么的
  27. */
  28. var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2;
  29. /**
  30. * @description Symbol.for('react.provider') React.context.provider组件标识
  31. */
  32. var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd;
  33. /**
  34. * @description Symbol.for('react.context') React.context组件标识
  35. */
  36. var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace;
  37. var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for('react.concurrent_mode') : 0xeacf;
  38. var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
  39. var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for('react.suspense') : 0xead1;
  40. var REACT_SUSPENSE_LIST_TYPE = hasSymbol ? Symbol.for('react.suspense_list') : 0xead8;
  41. var REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3;
  42. var REACT_LAZY_TYPE = hasSymbol ? Symbol.for('react.lazy') : 0xead4;
  43. var REACT_FUNDAMENTAL_TYPE = hasSymbol ? Symbol.for('react.fundamental') : 0xead5;
  44. var REACT_RESPONDER_TYPE = hasSymbol ? Symbol.for('react.responder') : 0xead6;
  45. /**
  46. * @description 返回一个对象的Iterator方法,一般形参是一个array
  47. * @description 如果Symbol函数存在,就将如参的maybeIterable[Symbol.iterator]方法返回
  48. * @param {*} maybeIterable
  49. */
  50. function getIteratorFn(maybeIterable) {
  51. if (maybeIterable === null || typeof maybeIterable !== 'object') {
  52. return null;
  53. }
  54. var maybeIterator = typeof Symbol === 'function' && maybeIterable[Symbol.iterator] || maybeIterable['@@iterator'];
  55. if (typeof maybeIterator === 'function') {
  56. return maybeIterator;
  57. }
  58. return null;
  59. }
  60. var a = [1, 2, 3, 4];
  61. var iterFn = getIteratorFn(a);
  62. var b = iterFn.apply(a);
  63. console.log(b.next());

二 阅读源码所需要的基本能力

属性与属性描述符

一个对象具有三种类型的属性或方法,可枚举属性或方法,不可枚举属性或方法,
Symbol属性或方法。
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object

Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。

**Object.seal()**方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要可写就可以改变。

**Object.preventExtensions()**方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。

  1. Object的常用方法的使用
  2. Object.prototype.hasOwnProperty; 此方法可以验证一个key是否是一个对象的属性或方法
  3. Object.prototype.propertyIsEnumerable; 检测一个属性是否是可枚举属性
  4. Object.assign; 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
  5. Object.getOwnPropertyNames 返回目标对象的可枚举属性与不可枚举属性的属性名组成的数组。
  6. Object.defineProperty 设置属性描述符,一次只能设置一个属性
  7. Object.defineProperties 批量设置属性描述符,可以一次性设置多个属性
  8. Object.seal 密封一个对象
  9. Object.getOwnPropertyDescriptor 返回目标对象的对应属性的属性描述符
  10. Object.getOwnPropertySymbols; 此方法返回对象的Symbol的属性与方法
  11. Object.freeze 冻结一个对象
  12. Object.preventExtensions 不可扩展
  13. Object.keys 返回一个对象的key组成的数组

对象每个属性都有属性描述符或存取描述符,详细可以参考MDN https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

image.png

image.png

Object.assign 的 Polyfill方法

image.png


继承链


通过验证实例的原型或者使用instanceof,验证实例是否属于某个类的,
例如React.Component的实例

原型链

函数实例


每个函数的实例都具有constructor属性,此属性会返回一个constructor对象,此对象中的prototype指向函数的原型对象。
image.png

可以通过Object.getPrototypeOf,Object.setPrototypeOf设置或获取某个实例或函数的原型对象

函数实例查找属性和方法的方式是,首先查找自身,然后一级一级向父级的函数原型对象上查找,直到null为止。
你可以通过proto 一直向上查找就会找到null了,但是这个属性并非标准的属性,而是浏览器厂商支持的,你可以通过Object.getPrototypeOf方式获取到实例的函数原型对象。

函数本身

静态属性
在ES6中class创建的类,有一个static的关键字,可以向函数声明属性和方法,不会被函数的实例查找到。

image.png

函数的原型对象

函数的原型上所有的属性和方法都可以被实例所使用,并且函数原型上具有一个prototype.constructor会指向函数本身

extends

  1. var A = function() {};
  2. A.prototype.a = 1;
  3. var B = function() {
  4. this.b = 2;
  5. };
  6. B.prototype = Object.create(A.prototype);
  7. B.prototype.constructor = B;
  8. var b = new B();
  9. console.log(b.a, b.b);
  10. console.log(B instanceof A) // false
  11. console.log(A instanceof A) // true

new 如何实现

  1. var a1 = Object.setPrototypeOf({ }, A.prototype);
  2. A.apply(a1, []);

bind的实现

  1. Function.prototype._bind = function(){
  2. var self = this //原函数
  3. var context = [].shift.call(arguments) //this上下文
  4. var args = [].slice.call(arguments) //参数
  5. return function(){
  6. self.apply(context, args.concat([].slice.call(arguments)))
  7. }
  8. }
  9. if(!Function.prototype.bind){
  10. Function.prototype.bind = function(oThis){
  11. if(typeof this !== 'function'){
  12. throw new TypeError('被绑定的对象需要是函数')
  13. }
  14. var self = this
  15. var args = [].slice.call(arguments, 1)
  16. var func = function(){}
  17. fBound = function(){ // instanceof用来检测某个实例对象的原型链上是否存在这个构造函数的prototype属性,this instanceof func === true时,说明返回的fBound被当做new的构造函数调用,此时this=fBound(){},否则this=window, 如果是的话使用新创建的this代替硬绑定的this
  18. return self.apply(this instanceof func ? this : oThis, args.concat([].slice.call(arguments)))
  19. }
  20. //维护原型关系
  21. if(this.prototype){
  22. func.prototype = this.prototype
  23. }
  24. //使fBound.prototype是func的实例,返回的fBound若作为new的构造函数,新对象的__proto__就是func的实例
  25. fBound.prototype = new func()
  26. return fBound
  27. }
  28. }