读了会 axios 源码,虽然云里雾里,但是我想到了三个有趣的对比

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

源码阅读

最近翻来了 axios 源码信心满满的看了会虽然哪跟哪都没串起来但是意外收获了一些新的想法。

有几组不错的知识点对比看比单独看每个知识点更有趣一些。

遇到有趣的知识点当然要分享一下。

文章速读

本文从 axios 的源码联想到了几个不错的知识点对比。

阅读文章可以有以下收获:

知识点对比开始

delete or undefined, 谁是更好的选择?

技术讨论小剧场

一:这里为什么要用 delete 删除这个属性。

某:因为异步请求里不能带上这个属性。

一:把值设置成 undefinedJSON.stringify 方法会过滤掉值为 undefined 的对象属性。

某:这个方法好像挺不错的。等等我要是没有用到 JSON.stringify 方法呢?

一:写个判断方法。

某:键盘已递上。

undefined 值的判断方法

const typeOfTest = type => thing => typeof thing === type;

/**
 * Determine if a value is undefined
 *
 * @param {*} val The value to test
 *
 * @returns {boolean} True if the value is undefined, otherwise false
 */
const isUndefined = typeOfTest('undefined');

const val = undefined;
const val2 = 2;
isUndefined(val) // true
isUndefined(val2) // false 

某:怎么写的这么快

一:当然了这可是 axios 源码里采用的方法。

某:所以结论就是推荐将对象的值设置为 undefined而不是delete 它。

一:是的。

某:再讲点 delete 的知识点。这个操作符我还不太熟悉。

一:这个可以有。正好可以正向思维和逆向思维齐上阵。

delete 删除了什么?

删除对象的某个属性

我们会按照 MDN 文档定义的那样「delete 操作符用于删除对象的某个属性」先来尝试删除对象的属性:

var obj = {name: 'ye',age: 18,
};
delete obj.age;
console.log(delete obj.age); // true
console.log(obj); // { name: 'ye' } 

非常符合预期的结果delete 返回值是 true且 obj 已不存在 age 属性。

如果删除一个对象不存在的属性会怎样?

var obj = {name: 'ye',age: 18,
};
delete obj.desc;
console.log(delete obj.desc); // true
console.log(obj); // { name: 'ye', age: 18 } 

这种情况也很好解释MDN 官网已经给了文字解释:

如果你试图删除的属性不存在那么 delete 将不会起任何作用但仍会返回 true。

delete ‘zhang’ or delete 18 结果会怎样?

console.log(delete 'zhang'); // true
console.log(delete 18); // true 

结果都是 true,有意思。其实这里的字符串也好数字也好其实是单值表达式的结果delete 删除的是这个表达式的结果所以返回了 true。

所谓单值表达式是指没有运算符的表达式一般值是其本身。

删除 let 或 const 声明的属性会怎样?

let name = 'ye';
or 
let name = obj.name;
console.log(delete name); // false 

结果是 false,因为任何用 let 或 const 声明的属性不能够从它被声明的作用域中删除。

小结

1.不需要某个对象属性时推荐将对象的值设置为 undefined而不是delete 它。
2.经过一系列实验会发现delete 其实删除的是一个表达式的引用类型的结果。
3.axios 源码内容虽然不是特别多但是完整的梳理清楚还是挺耗时的。我帮大家画个入口示意图感兴趣的可以下载到本地慢慢赏析。☞axios github

yield 和 return这俩有故事?

技术讨论小剧场

一:为何眉头紧锁。

某:正在看 axios 的源码。

一:我理解了。

某:我不理解这里为什么要用 yield。

一: 这么快看到这里了。

某:我只是先打开了这个文件而已。

品品为什么要用 yield

axios 源码里面有一段代码在测试用例里面实际的代码无法直接打印我稍作了调整如下:

const count = 10;
const chunkLength = 10;
const contentLength = count * chunkLength;
const samples = Array.from((function* () {for (let i = 1; i <= 10; i++) {yield {loaded: chunkLength * i,total: contentLength,progress: (chunkLength * i) / contentLength,bytes: 4,download: true,};}})(),
); 

打印 samples 的结果

某:如果这里的代码把 yield 改成 return 会怎样。

一:你猜。

不卖关子其实会打印一个空数组。

console.log(samples); // [] 

某:能用 return 实现上面的截图中的数组吗?

一:当然可以。

某:键盘和膝盖双双奉上。

Array.from 支持通过伪数组对象创建数组对象。伪数组对象是指拥有一个 length 属性和若干索引属性的任意对象。

const count = 10;
const chunkLength = 10;
const contentLength = count * chunkLength;
const samples = Array.from({ length: count }, (_, j) => {let i = j + 1;return {loaded: chunkLength * i,total: contentLength,progress: (chunkLength * i) / contentLength,bytes: 4,download: true,};
}); 

yield 和 return 的故事

相同场景

这俩功能看着挺相似的都能返回值给函数调用者。比如下面这两个代码块打印的结果是一样的。

使用 yield

function* foo(index) {while (index < 2) {index++;yield index;}
}

const iterator = foo(0);

console.log(iterator.next().value); // 1 

使用 return

function* foo(index) {while (index < 2) {index++;return index;}
}

const iterator = foo(0);

console.log(iterator.next().value); // 1 

如果把返回的这行代码提升会怎么样?

使用 yield

function* foo(index) {while (index < 2) {yield index;index++;}
}

const iterator = foo(0);

console.log(iterator.next().value); // 0
console.log(iterator.next().value); // 1 

使用 return

function* foo(index) {while (index < 2) {return index;index++;}
}

const iterator = foo(0);

console.log(iterator.next().value); // 0
console.log(iterator.next().value); // undefined 

结果不一样了。可见这俩有个明显的不同:

  • return 语句终止函数的执行。
  • yield 语句挂起当前函数而下一次调用 next() 时在 yield 之后紧接着的语句会继续执行

听说 yield 能传值?

来看下面这个例子

function* func(x) {console.log('x:', x);x--;x = yield x;
}
let generatorFunc = func(10);
let value = generatorFunc.next().value;
console.log('value:', value); 

打印结果

x: 10 // 第一个console
value: 9 // 第二个console 

解析一下第一个 consolex 值来自函数的传参这个很容易理解。第二个 console这里 x 值来自调用 generatorFunc.next(arg) 时的传参 arg而 arg 来自 yield 表达式的结果。

小结

1.yield 关键字用于暂停和恢复生成器函数。
2.yield 和 return 的故事在于yield可以被认为是一个基于生成器的版本的 return 关键字。但是 yield 功能更丰富。
3.axios 源码看到哪里算哪里吧。

typeof and toString, 类型校验专场

技术讨论小剧场

某:还记得我们前面讨论的如何判断值为 undefined 吗?

一:当然就在文章前面没多远的地方。

某:我又找到了一种判断方式。

一:替换方式?哪种更好?

某:我愿称之为 typeOfTest 的升级版。

toString:更辽阔的类型校验

先来看看 axios 源码是怎么实现对引用类型(其实包括基础类型的校验。

const { toString } = Object.prototype;
const kindOf = (cache => thing => {const str = toString.call(thing);return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase());
})(Object.create(null));

const kindOfTest = type => {type = type.toLowerCase();return thing => kindOf(thing) === type;
};
/**
 * Determine if a value is a Date
 *
 * @param {*} val The value to test
 *
 * @returns {boolean} True if value is a Date, otherwise false
 */
const isDate = kindOfTest('Date');

console.log(isDate(new Date())); // true
console.log(isDate('ye')); // false 

上面的方法核心在于使用 Object.prototype.toString.call() 获取数据类型它返回 “[object Type]”使用正则匹配可以得到最终的 Type。

下面我们用 Object.prototype.toString.call() 打印一下绝大多数的数据类型看结果否和我们预期的一样:

console.log(Object.prototype.toString.call(111)); // [object Number]
console.log(Object.prototype.toString.call('ye')); // [object String]
console.log(Object.prototype.toString.call(true)); // [object Boolean]
console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call(undefined)); // [object ReUndefinedgExp]
console.log(Object.prototype.toString.call(Symbol())); // [object Symbol]
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call(function () {})); // [object Function]
console.log(Object.prototype.toString.call(new Error())); // [object Error]
console.log(Object.prototype.toString.call(new Date())); // [object Date]
console.log(Object.prototype.toString.call(new RegExp())); // [object RegExp]
console.log(Object.prototype.toString.call(Map)); // [object Function]
console.log(Object.prototype.toString.call(Math)); // [object Math] 

可以得到几乎所有的数据类型。

小结

1.Object.prototype.toString.call() 几乎可以实现所有数据类型的校验。
2.想要获取准确的数据类型还要在 [object Type] 的基础上通过正则匹配获取最终的 Type。
3.方法都是现成的需要哪个用哪个。掌声送给 axios 源码。

最后

最近找到一个VUE的文档它将VUE的各个知识点进行了总结整理成了《Vue 开发必须知道的36个技巧》。内容比较详实对各个知识点的讲解也十分到位。



有需要的小伙伴可以点击下方卡片领取无偿分享

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

“读了会 axios 源码,虽然云里雾里,但是我想到了三个有趣的对比” 的相关文章