一、检测数据类型

1. typeof

typeof:用来检测数据类型的 typeof[value] return: 首先是个字符串:字符串中包含对应的数据类型,例如”number”、”object”、”undefined”、”function” 局限性:

  1. typeof null // object
    不能具体区分对象数据类型的值(无法检测正则还是数组等)
  2. typeof [] // object
  3. typeof {} // object
  4. typeof /^&/ // object

优势:使用方便,所以在真实项目中,我们也会大量应用它来检测,尤其是在检测基本数据类型值(除 null 以外)和函数数据类型值的时候,它还是很方便的

  1. function fun (n, m) {
  2. // 形参赋值默认值
  3. // ES6 func (n = 0, m = 0)
  4. // 检测形参的值是否为undefined
  5. n === undefined ? n : null;
  6. typeof m === "undefined" ? m = 0 : null;
  7. // 基于逻辑或和逻辑与处理,(瑕疵:不仅仅是不传赋值默认值,如果传递的值是假也会处理成为默认值
  8. n = n || 0;
  9. m = m || 0;
  10. // 回调函数执行
  11. typeof callback === "function" ? callback() : null;
  12. callback && callback()
  13. // 瑕疵:传递为真即可执行,不一定是一个函数,这样写是开发者心里已经知道,要不然不传,要传就是一个函数
  14. }
  15. func (10, 20, function anonymous () {});

instanceof

instanceof:本意是用来检测实例是否隶属于某个类的运算符,我们基于这种方式,也可以用来做某些数据类型的检测,例如:数组、正则等。

局限性:

  1. 不能处理基本数据类型
  2. 只要在当前实例的原型链中(proto)出现过的类,检测结果都是 ture (用户可能会手动修改原型链的指向:example.proto 或者 在类的继承中 等情况)
  1. function func () {
  2. // arguments:类数组
  3. arguments.__proto__ = Array.prototype;
  4. console.log(arguments instanceof Array) // ture
  5. }
  6. func();
  7. let arr = [],
  8. reg = /^&/,
  9. obj = {};
  10. console.log(arr instanceof Array); // true
  11. console.log(reg instanceof Array); // false
  12. console.log(arr instanceof Object); // true
  13. console.log(obj instanceof Array); // false
  14. // 检测基本数据类型
  15. console.log(1 instanceof Number); // false
  16. // 创建值的两种方式
  17. // 字面量
  18. let n = 12;
  19. // 构造函数
  20. let m = new Number('12');

constructor

constructor:构造函数

在类的原型上一般都会有 constructor 属性,存储当前类本身,我们也是利用这一点,获取某类的实例constructor 属性值,验证是否为所属的类,从而进行数据类型检测

局限性:constructor 的值太容易被修改了

  1. let n = 12,
  2. arr = [];
  3. console.log(n.constructor === Number); // true
  4. console.log(arr.constructor === Array); // true
  5. console.log(arr.constructor === Object); // false
  6. arr.constructor === 111; // 设置私有属性
  7. console.log(arr.constructor === Array); // false
  8. Func.prototype = {}; // 这样原型上没有 constructor 属性(重构了)

Objcet.prototype.toString.call(value)

Objcet.prototype.toString.call(value) : 原理是调用 Object 原型上的 toString 方法,让方法执行的时候,方法中的 this 是要检测的数据类型,从而获取到数据类型所属类的详细信息

信息的模板: “[objcet 所属类]” , 例如: “[object Array]” …

在所有的数据类型类中,他们的原型上都有 toString 方法,除 Objcet.prototpye.toString。不是把数据类型转换为字符串,其余的都是转换为字符串,而 Object 的原型上的 toString 是检测当前实例隶属类的详细信息的。

obj.toString();

  1. 首先基于原型链查找机制,找到 object.prototype.toString
  2. 把找到的方法执行,方法中的 this:obj
  3. 方法内部把 this(obj) 所属类信息输出
  4. 方法执行:方法中的 this 是谁,就是检测谁的所属类信息
  1. let _obj = {};
  2. console.log(_obj.toString.call(100)); // [object Number]
  3. Objcet.prototype.toString.call(100); // 两者等价
  4. function func (n, m) {
  5. return n + m;
  6. }
  7. let obj1 = {},
  8. obj2 = {
  9. name:'zhufeng'
  10. };
  11. obj1.toSrring();
  12. console.log([12, 23].toString()); // 12, 23
  13. console.log(/^\d+$/.toString()); // /^\d+$/
  14. console.log(func.toString()); // "function func(n, m) {..."
  15. console.log(obj1.toString()); // "[object Object]"
  16. console.log(obj2.toString()); // "[object Object]"
  17. 这个方法很强大
  18. 所有数据类型隶属的类信息检测的一清二楚
  19. let _obj = {},
  20. toString = _obj.toString
  21. [object '数据类型']
  22. // String Boolean Null Undefined ...

二、封装一个数据类型检测的方法库

  1. var _obj = {
  2. isNumeric: "Number",
  3. isBoolean: 'Boolean',
  4. isString: 'String',
  5. isNull: 'Null',
  6. isUndefined: 'Undefined',
  7. isSymbol: 'Symbol',
  8. isPlainObject: 'Object',
  9. isArray: 'Array',
  10. isRegExp: 'RegExp',
  11. isDate: 'Date',
  12. isFunction: "Function",
  13. isWindow: 'Window'
  14. },
  15. _toString = _obj.toString,
  16. _type = {};
  17. for (var key in _obj) {
  18. if (!_obj.hasOwnProperty(key)) break;
  19. _type[key] = (function () {
  20. var reg = new RegExp("^\\[object " + _obj[key] + "\\]$");
  21. return function anonymous(val) {
  22. return reg.test(_toString.call(val));
  23. }
  24. })();
  25. };

三、回调函数

把一个函数当作实参传递个另外一个函数,在另外一个函数执行过程当中,把传递进来的函数执行,这种机制就是回调函数 真实场景应用:

  1. AJAX 异步请求成功做什么事
  2. 浏览器内置的一些方法支持回调函数
  3. 插件组件封装中的钩子函数(生命周期函数)

四、一个综合的 each 方法

  1. /*
  2. * _each:遍历数组、类数组、对象中的每一项
  3. * @params
  4. * obj:需要迭代的数组、类数组、普通对象
  5. * callback:回调函数(每遍历数组中的某一项,就会把回调函数执行一次;而且需要把当前遍历的内容和索引[属性值和属性名]传给回调函数;接收回调函数的返回结果,如果是 false,则结束当前的循环,如果是其它值,让返回的值替换数组中的当前项,如果没有返回值,则什么都不处理...)
  6. * context:传递的第三个参数,可以改变回调函数中的 THIS 指向,不传递默认是 window
  7. * @return
  8. * 返回一个新的数组或者对象(原来的数组或者对象不变)
  9. */
  10. function _each(obj, callback, context = window) {
  11. let isLikeArray = _type.isArray(obj) || (('length' in obj) && _type.isNumeric(obj.length));
  12. typeof callback !== "function" ? callback = Function.prototype : null;
  13. // 数组或者类数组
  14. if (isLikeArray) {
  15. let arr = [...obj];
  16. for (let i = 0; i < arr.length; i++) {
  17. let item = arr[i],
  18. result = callback.call(context, item, i);
  19. if (result === false) break;
  20. if (typeof result === "undefined") continue;
  21. arr[i] = result;
  22. }
  23. return arr;
  24. }
  25. // 对象的处理
  26. let opp = {
  27. ...obj
  28. };
  29. for (let key in opp) {
  30. if (!opp.hasOwnProperty(key)) break;
  31. let value = opp[key],
  32. result = callback.call(context, value, key);
  33. if (result === false) break;
  34. if (typeof result === "undefined") continue;
  35. opp[key] = result;
  36. }
  37. return opp;
  38. }

五、重写 replace 方法

  1. /*
  2. * 重写字符串内置方法 replace
  3. * 1.正则在字符串中匹配几次,我们传递的回调函数就会被执行几次(前提:正则设置了 global 修饰符)
  4. * 2.每一次执行回调函数,都把当前正则匹配的信息(既有大正则,也有小分组的)传递给回调函数
  5. * 3.还要接收回调函数的返回值,返回的是啥内容,就要把当前字符串中正则匹配这一部分内容替换成啥
  6. */
  7. ~ function () {
  8. // 处理字符串:把字符串中的某一项替换成另外一项
  9. function handle(str, val1, val2) {
  10. let index = str.indexOf(val1);
  11. return str.substring(0, index) + val2 + str.substring(index + val1.length);
  12. }
  13. function replace(reg, callback) {
  14. let _this = this.substring(0),
  15. isGlobal = reg.global,
  16. arr = reg.exec(this);
  17. while (arr) {
  18. // 捕获到的结果是数组(执行回调函数,把捕获的结果传递给它);还要接收回调函数执行的返回值,用返回值替换字符串中当前正则匹配的内容;
  19. if (typeof callback === "function") {
  20. let res = callback.apply(null, arr);
  21. _this = handle(_this, arr[0], res);
  22. }
  23. arr = reg.exec(this);
  24. // 不设置 GLOBAL 的情况执行一次
  25. if (!isGlobal) break;
  26. }
  27. return _this;
  28. }
  29. String.prototype.replace = replace;
  30. }();
  31. let str = "{0}年{1}月{2}日",
  32. arr = ['2019', '09', '03'];
  33. str = str.replace(/\{(\d)\}/g, function (content, group1) {
  34. return '@#' + arr[group1];
  35. });
  36. console.log(str);