zoukankan      html  css  js  c++  java
  • Underscore _.template 方法使用详解

    为什么用「void 0」代替「undefined」
    undefined 并不是保留词(reserved word),它只是全局对象的一个属性,在低版本 IE 中能被重写。

    事实上,undefined 在 ES5 中已经是全局对象的一个只读(read-only)属性了,它不能被重写。但是在局部作用域中,还是可以被重写的。

    void 运算符能对给定的表达式进行求值,然后返回 undefined MDN

    常用类型判断以及一些有用的工具方法
    _.isArray = nativeIsArray || function(obj) {
    return toString.call(obj) === '[object Array]';
    };

    // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError.
    // 其他类型判断
    _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) {
    _['is' + name] = function(obj) {
    return toString.call(obj) === '[object ' + name + ']';
    };
    });
    使用 Object 的原型方法 toString()来获取 obj 的字符串表示,形式是 [object class]

    // Is the given value NaN? (NaN is the only number which does not equal itself).
    _.isNaN = function(obj) {
    return _.isNumber(obj) && obj !== +obj;
    };
    原生 isNaN 方法存在怪异行为:如果 isNaN 函数的参数不是 Number 类型, isNaN() 会首先尝试将这个参数转换为数值,然后才会对转换后的结果是否是 NaN 进行判断。MDN

    for ... in 存在的浏览器兼容问题你造吗
    // IE < 9 下不能用 for in 来枚举的 key 值集合
    var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
    'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
    内部方法 createAssigner 详解
    // for-in 会遍历出一个对象从其原型链上继承到的可枚举属性
    var allKeys = function (obj) {
    var keys = [];
    for (var key in obj) keys.push(key);
    return keys;
    };

    var keys = Object.keys
    
    var createAssigner = function (keysFunc, undefinedOnly) {
        return function (obj) {
            var length = arguments.length;
            if (length < 2 || obj == null) return obj;
            for (var index = 1; index < length; index++) {
                var source = arguments[index],
                    keys = keysFunc(source),
                    l = keys.length;
                for (var i = 0; i < l; i++) {
                    var key = keys[i];
                     // undefinedOnly 参数为 true, 即 !undefinedOnly 为 false
                    // 那么当且仅当 obj[key] 为 undefined 时才覆盖
                    // 即如果有相同的 key 值,取最早出现的 value 值
                    if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
                }
            }
            return obj;
        };
    };
    
    var a = {a: 'hello'}
    function Super(name) {
        this.name = name;
    }
    Super.prototype.superman = 'superman';
    
    var b = new Super('world')
    var c = {c: 'hello'}
    
    
    console.log(createAssigner(allKeys)(a, b)) // {a: "hello", name: "world", superman: "superman"}
    console.log(createAssigner(keys)(c, b)) // {c: "hello", name: "world"}
    

    createAssigner 返回了一个函数,这个返回的函数引用了外面的一个变量,这是一个典型的闭包。这有利于设计用途类似的 API。

    JavaScript 中是如何比较两个元素是否 "相同" 的
    普通类型判断

    // 0 和 -0 被认为是不相等, 1/0 === 1/-0 => false
    if (a === b) return a !== 0 || 1 / a === 1 / b;
    // 如果 a 和 b 有一个为 null(或者 undefined)
    if (a == null || b == null) return a === b;
    // 如果 a 和 b 类型不相同,则返回 false
    var className = toString.call(a);
    if (className !== toString.call(b)) return false;
    switch (className) {
        case '[object RegExp]':
        case '[object String]':
            // 转为 String 类型进行比较
            return '' + a === '' + b;
        case '[object Number]':
            // NaN 与 NaN 相等
            if (+a !== +a) return +b !== +b;
            return +a === 0 ? 1 / +a === 1 / b : +a === +b;
        case '[object Date]':
        case '[object Boolean]':
            return +a === +b;
    }
    

    Array 和 Object 类型判断 [NOT Completed]

    Object Functions 相关源码拾遗 & 小结
    Object.create() Polyfill

    var create = (function () {
    //为了节省内存,使用一个共享的构造器
    function Temp() {
    }
    
    // 使用 Object.prototype.hasOwnProperty 更安全的引用
    var hasOwn = Object.prototype.hasOwnProperty;
    
    return function (O) {
        // 1. 如果 O 不是 Object 或 null,抛出一个 TypeError 异常。
        if (typeof O != 'object') {
            throw TypeError('Object prototype may only be an Object or null');
        }
    
        // 2. 使创建的一个新的对象为 obj ,就和通过
        //    new Object() 表达式创建一个新对象一样,
        //    Object是标准内置的构造器名
        // 3. 设置 obj 的内部属性 [[Prototype]] 为 O。
        Temp.prototype = O;
        var obj = new Temp();
        Temp.prototype = null; // 不要保持一个 O 的杂散引用(a stray reference)...
    
        // 4. 如果存在参数 Properties ,而不是 undefined ,
        //    那么就把参数的自身属性添加到 obj 上,就像调用
        //    携带obj ,Properties两个参数的标准内置函数
        //    Object.defineProperties() 一样。
        if (arguments.length > 1) {
            // Object.defineProperties does ToObject on its first argument.
            var Properties = Object(arguments[1]);
            for (var prop in Properties) {
                if (hasOwn.call(Properties, prop)) {
                    obj[prop] = Properties[prop];
                }
            }
        }
    
        // 5. 返回 obj
        return obj;
    };
    

    })();
    _.create 方法思路大致如此:

    var baseCreate = function(prototype) {
    if (!_.isObject(prototype)) return {};
    if (nativeCreate) return nativeCreate(prototype);
    Ctor.prototype = prototype;
    var result = new Ctor;
    Ctor.prototype = null;
    return result;
    };

    _.create = function(prototype, props) {
    var result = baseCreate(prototype);
    if (props) _.extendOwn(result, props);
    return result;
    };
    如何优雅地写一个「在数组中寻找指定元素」的方法
    [NOT Completed]

    JavaScript 数组去重
    双重循环法:

    function unique(arr) {
    var res = [];
    var isRepeat;
    for (var i = 0; i < arr.length; i++) {
    isRepeat = false;
    for (var j = i + 1; j < arr.length; j++) {
    if (arr[i] === arr[j]) {
    isRepeat = true;
    break;
    }
    }
    if (!isRepeat) {
    res.push(arr[i]);
    }
    }
    return res;
    }
    ES5:

    function unique(arr) {
    return arr.filter(function (item, index, array) {
    // indexOf()方法返回给定元素能找在数组中找到的第一个索引值
    return array.indexOf(item) === index;
    });
    }
    ES6:

    function unique(arr) {
    return Array.from(new Set(arr));
    }
    JavaScript 数组展开以及 underscore 重要的内部方法 flatten 详解
    双重循环法:

    function flatten(arr) {
    var result = [];
    for (var i = 0; i < arr.length; i++) {
    for (var j = 0; j < arr[i].length; j++) {
    result.push(arr[i][j]);
    }
    }
    return result;
    }
    apply + concat:

    function flatten(arr) {
    return Array.prototype.concat.apply([], arr);
    }
    MDN

    多维数组:

    function flatten(arr) {
    var tmp = arr;
    var result = arr;
    while(tmp instanceof Array) {
    result = Array.prototype.concat.apply([], result);
    tmp = tmp[0];
    }
    return result;
    }

    flatten([[[1, 2], [1, 2, 3]], [1, 2]]) // [1, 2, 1, 2, 3, 1, 2]
    Array Functions 相关源码拾遗 & 小结
    [NOT Completed]

    你可能不知道的 NaN 以及 underscore 1.8.3 _.isNaN 的一个 BUG
    var isNaN1 = function (obj) {
    return obj !== obj;//错误:new Number(NaN) => false
    };
    var isNaN2 = function (obj) {
    return _.isNumber(obj) && obj !== +obj;//错误:new Number(0) => true
    };
    var isNaN3 = function (obj) {
    return _.isNumber(obj) && isNaN(obj);//正确:new Number(NaN) => true
    };
    对于NaN的判断,如果只针对Number类型,用 underscore 最新版的 _.isNaN3 判断完全没有问题,
    或者用 ES6 的 Number.isNaN,两者的区别就在于一个 new Number(NaN)

    Number.isNaN(new Number(NaN)) => false
    JavaScript 特殊对象 Array-Like Objects 详解
    ArrayLike 类数组需满足两点要求:

    有 length 属性
    length 为非负 Number 类型
    var isArrayLike = function(collection) {
    var length = getLength(collection);
    return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
    };
    ArrayLike to Array:

    function fn() {
    var arr = [].slice.call(arguments);
    arr.push(4); // arr -> [1, 2, 3, 4]
    }
    MDN

    ES6:

    var str = "helloworld";
    var arr = Array.from(str);
    // ["h", "e", "l", "l", "o", "w", "o", "r", "l", "d"]
    MDN

    JavaScript 数组乱序
    splice 方法:

    function shuffle(a) {
    var b = [];

    while (a.length) {
    //https://coderwall.com/p/9b6ksa/is-faster-than-math-floor
    var index = ~~(Math.random() * a.length);
    b.push(a[index]);
    a.splice(index, 1);
    }

    return b;
    }
    sort 方法:

    function shuffle(a) {
    // concat 复制原数组
    return a.concat().sort(function (a, b) {
    return Math.random() - 0.5;
    });
    }
    注意:Array.prototype.sort 不能做到完全随机,这取决于排序算法的实现

    Fisher–Yates Shuffle:

    // 我们每一次循环从前 len - i 个元素里随机一个位置,将这个元素和第 len - i 个元素进行交换,迭代直到 i = len - 1 为止
    // 比如长度为5的数组,第一次循环从前五个元素中随机一个和第五个元素交换,
    // 第二次从前四个元素中随机一个和第四个元素交换
    function shuffle(a) {
        var arr = a.concat()
        var len = arr.length;
        for (var i = 0; i < len - 1; i++) {
            var idx = ~~(Math.random() * (len - i));
            var temp = arr[idx];
            arr[idx] = arr[len - i - 1];
            arr[len - i - 1] = temp;
        }
        return arr;
    }   
    

    浅谈 underscore 内部方法 group 的设计原理
    [NOT Completed]

    关于 bind 你可能需要了解的知识点以及使用场景
    bind()方法会创建一个新函数。当这个新函数被调用时,bind()的第一个参数将作为它运行时的 this, 之后的一序列参数将会在传递的实参前传入作为它的参数。

    偏函数与柯里化

    函数柯里化的本质是,可以在调用一个函数的时候传入更少的参数,而这个函数会返回另外一个函数并且能够接收其他参数。是一种实现多参数函数的方法。

    function add(x){
    return function(y){
    return x + y;
    }
    }
    var inc = add(1)
    var dev = add(-1)
    inc(1) // 2
    dev(1) // 0
    偏函数应用到了 bind ,他解决这样的问题:如果我们有函数是多个参数的,我们希望能固定其中某几个参数的值。

    function list() {
    return Array.prototype.slice.call(arguments);
    }

    var list1 = list(1, 2, 3); // [1, 2, 3]

    // Create a function with a preset leading argument
    var leadingThirtysevenList = list.bind(undefined, 37);

    var list2 = leadingThirtysevenList(); // [37]
    var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
    bind 方法的兼容实现
    [NOT Completed]

    JavaScript 函数去抖和函数节流应用场景辨析
    通过 RxMarbles(珠宝图) 可以形象地理解函数去抖和函数节流

    debounce
    throttle
    throttle 和 debounce 的应用场景:

    (1)按一个按钮发送 AJAX:给 click 加了 debounce 后就算用户不停地点这个按钮,也只会最终发送一次;如果是 throttle 就会间隔发送几次

    (2)监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;
    如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次

    underscore 函数去抖的实现
    函数去抖就是对于一定时间内的连续的函数调用,只让其执行一次。核心思想是重复添加定时器。

    简单实现:

    function debounce(func, wait) {
    // 闭包缓存 timeout
    var timeout, result;
    // 返回函数
    return function () {
    var context = this, args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(function () {
    timeout = null;
    result = func.apply(context, args);
    }, wait);
    return result;
    };
    }
    immediate 为 true, debounce会在 wait 时间间隔的开始调用这个函数

    _.debounce = function(func, wait, immediate) {
    var timeout, args, context, timestamp, result;

    var later = function() {
      var last = _.now() - timestamp;
    
      if (last < wait && last >= 0) {
        timeout = setTimeout(later, wait - last);
      } else {
        timeout = null;
        if (!immediate) {
          result = func.apply(context, args);
          if (!timeout) context = args = null;
        }
      }
    };
    
    return function() {
      context = this;
      args = arguments;
      timestamp = _.now();
      var callNow = immediate && !timeout;
      if (!timeout) timeout = setTimeout(later, wait);
      if (callNow) {
        result = func.apply(context, args);
        context = args = null;
      }
    
      return result;
    };
    

    };
    function print() {
    console.log('hello world');
    }

    window.onscroll = _.debounce(print, 1000);
    underscore 函数节流的实现
    function throttle(func, wait) {
    var timeout, result;
    return function () {
    var context = this, args = arguments;
    // 如果不存在定时器,则设置定时器
    if (!timeout)
    timeout = setTimeout(function () {
    // 执行完成清空定时器
    timeout = null;
    result = func.apply(context, args);
    }, wait);
    return result;
    };
    }
    从斐波那契数列求值优化谈 _.memoize 方法
    [NOT Completed]

    Function Functions 相关源码拾遗
    [NOT Completed]

    浅谈 Web 中前后端模板引擎的使用
    后端 MVC

    后端 MVC 模式中,一般从 Model 层中读取数据,然后将数据传到 View 层渲染(渲染成 HTML 文件),而 View 层,一般都会用到模板引擎。比如PHP 的 smarty 模板引擎。

    前端模板

    假设接口数据如下:

    [{name: "apple"}, {name: "orange"}, {name: "peach"}]
    渲染后的页面如下:

    • apple
    • orange
    • peach
    前端模板引擎出现之前,我们一般会这么做:

    将 HTML 代码(View 层)和 JS 代码(Controller 层)混杂在了一起很容易出错也不利于维护。所以前端模板引擎出现了:

    Node 中间层

    模板引擎虽然降低了耦合度,但是却不利于 SEO。我们可以让 Node 作为中间层。简单地说就是让一门后台语言提供接口,Node 中间层用模板引擎来渲染页面,使得页面直出。不失为一种比较好的解决方案。

    作者:bigggge
    链接:https://www.jianshu.com/p/92ea72e998c3
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 相关阅读:
    linux 命令学习
    反编译学习一:Mac下使用dex2jar
    如何删除你的MacOS上的旧版本Java
    使用screen 遇到的多窗口
    dede简略标题调用标签
    JQ实现导航滚动到指定位置变色,并置顶
    JQ实现当前页面导航加效果(栏目页有效)
    wordpress首页调用指定分类下的文章
    作业1#python用列表实现多用户登录,并有三次机会
    python数据类型之间的转换
  • 原文地址:https://www.cnblogs.com/torchstar/p/12131777.html
Copyright © 2011-2022 走看看