奇舞 javaScript-函数- 笔记

函数

  • 函数声明,函数表达式
  • 匿名函数
  • 函数参数
  • 作用域,闭包,this
  • apply,call, bind
  • 关于异步回调
  • 函数的动态构建
  • 函数式编程
  • 如何封装好的函数

    函数声明和函数表达式

  • 函数声明会被提升hoisting
    console.log([typeof add, typeof sub]);  // ["function", "undefined"]

    function add(x, y) {
        return x + y;
    }

    var sub = function(x, y){
       return x - y;
    }

    console.log([add(1, 2), sub(1, 2)]);  // [3, -1]

箭头函数表达式

let double = x => x * 2;

let add = (x, y) => {
    return x + y;
}

console.log([double(21), add(1, 2)]); //[42, 3]

匿名函数

在函数表达式和回调函数汇总常见

匿名函数与arguments.callee

function countDown(count, interval, callback) {
    if (count <= 0) return;

    callback(count);

    setTimeout(function() {
        if (--count > 0) {
            setTimeout(arguments.callee, interval);
        }
        callback(count);
    }, interval)
}

countDown(10, 1000, t => console.log(t));

//arguments.callee 指向当前函数本身

推荐使用具体名函数

function countDown(count, interval, callback) {
    if (count <= 0) return;

    callback(count);

    setTimeout(function update() {
        if (--count > 0) {
            setTimeout(update, interval);
        }
        callback(count);
    }, interval)
}

countDown(10, 1000, t => console.log(t));

函数的参数

  • 具名的参数, function.length
  • 可变参数与arguments
  • ES6 rest 参数
  • 参数默认值

function.length

// 函数过程抽象的方法 与传入的函数没有任何关系 都会做相同的处理

function __matchArgs__(fn) {
    return function(...args) {

        if (args.length !== fn.length) {
            throw RangeError('Arguments not __matchArgs__');
        }
        return fn.apply(this, args);
    }
}
var add = (a, b, c) => a + b + c;
var add = __matchArgs__((a, b, c) => a + b + c);



console.log(add(1, 2, 3));
console.log(add(4, 5));

可变参数与arguments

function add() {
    // Array.from 方法 把类数组的对象转换为数组
    // var args = [].slice.call(arguments);
    let args = Array.from(arguments);
    return args.reduce((a, b) => a + b);
}

console.log(add(1, 2, 3, 4)); //10



// JavaScript 函数涉及中经常会让参数允许有不同的类型

function setStyle(el, property, value) {
    if (typeof el === 'string') {
        el = document.querySelector(el);
    }

    if (typeof property === "object") {
        for (var key in property) {
            setStyle(el, key, property[key]);
        }
    } else {
        el.style[property] = value;
    }
}

console.log(setStyle.length);

setStyle('div', 'color', 'red');
setStyle('div', {
    "fontSize": "32px",
    "backgroundColor": 'white'
});

rest 参数

let add = (...args) => args.reduce((a, b) => a + b);

console.log(add(1, 2, 3, 4));

console.log(add.length);

function deepCopy(des, src) {
    if (!src || typeof src !== 'object') {
        return des;
    }
    for (var key in src) {
        let obj = src[key]
        if (obj && typeof obj === 'object') {
            des[key] = des[key] || {}

            deepCopy(des[key], obj);
        } else {
            des[key] = src[key];
        }
    }
    return des;

}

function merge(des, ...objs) {
    return [des, ...objs].reduce((a, b) => deepCopy(a, b))  
}

console.log(merge({ x: 1 }, { y: 2 }, { z: 3 }))  //{x: 1, y: 2, z: 3}

ES5 模拟rest 参数

// 函数变换

function __rest__(fn) {
    var len = fn.length;
    return function() {
        var args = [].slice.call(arguments, 0, len - 1);
        var rest = [].slice.call(arguments, length);
        return fn.apply(this, args.concat([rest]));
    }
}

var add = __rest__(function(args) {
    return args.reduce(function(a, b) {
        return a + b;
    });
});

console.log(add(1, 2, 3, 4));

参数默认值

// 参数默认值

function addMessage(message) {
    message = message || 'default message';

    var el = document.createElement('p');
    el.innerHTML = message;

    document.body.appendChild(el);
}

addMessage(); // default message

addMessage('hello world'); // 'hello world

function addMessage2(message = 'default message') {
    var el = document.createElement('p');
    el.innerHTML = message;

    document.body.appendChild(el);
}

addMessage2(); //default message
addMessage2('hello akira'); // hello akira

   function add(x, y = 0) {
           return x + y;
       }

    console.log(add(1)) ; //1


    function add(x, y = 0) {
        'use strict'
           return x + y;
       }

    console.log(add()) ; //SyntaxError: Illegal 'use strict' directive in function with non-simple parameter list

作用域, 闭包 , this

  • 重要: ES5用函数来构建作用域
  • IIFE: 立即执行函数
  • 什么是 ‘闭包’
  • JavaScript 的’this’
    for(var i = 0; i< 10; i++){
       // (function (i) {
           setTimeout(function(){
             console.log(i); // 10 10 10 10 10 10 10 10 10 10
           },100 * i)
       // })(i)
    }

  for(let i =0; i < 10; i++){
    setTimeout(function(){
       console.log(i) // 0 1 2 3 4 5 6 7 8 9
      }, 100 * i)
   }


    for(var i = 0; i< 10; i++){
       (function (i) {
           setTimeout(function(){
             console.log(i); // 0 1 2 3 4 5 6 7 8 9
           },100 * i)
       })(i)
    }

    for(var i = 0; i < 10; i++){
       setTimeout((function(i){
        console.log(i); // 0 1 2 3 4 5 6 7 8 9
       }).bind(null, i),100 * i); 
    }

闭包与私有数据

var MyClass = (function() {
    var privateDate = 'privateDate';

    function Class() {
        this.publicDate = 'publicDate';
    }
    Class.prototype.getDate = function() {
        return privateDate;
    }

    return Class;
})()


let Class;

{
    let privateDate = 'privateDate';
    Class = function() {
        this.publicDate = 'publicDate';
    }
    Class.prototype.getDate = function() {
        return privateDate;
    };

};




var myObj = new Class();

console.log(myObj);

console.log([myObj.publicDate, myObj.privateDate, myObj.getDate()]); // ["publicDate", undefined, "privateDate"]

“this” 问题

JavaScript 的 this 是由函数求值时的调用者决定的!!

    function Point2D(x, y) {
        this.x = x;
        this.y = y;
    }

    Point2D.prototype.showLength = function(){
        var length = Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
        console.log(length);
    }
       //  Async 异步
    Point2D.prototype.showLengthAsync = function(){
        var self = this;

        setTimeout(function(){
            self.showLength()
        }, 1000);
    }

    var x = 30; y = 40;

    var p = new Point2D(3, 4);

    var f = Point2D.prototype.showLength;

    f(); //50

    setTimeout(p.showLength, 500); //50

    p.showLengthAsync() //5 

apply, call,bind

  • 通过apply ,call 指定this调用函数
  • ES5 的bind
  • bind的重要意义和高级用法

call

function Poin2D(x, y) {
    this.x = x;
    this.y = y;
}

Poin2D.prototype.getLength = function() {
    return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
}

var p = new Poin2D(1, 1);

console.log(p.getLength()); // 1.4142135623730951

var obj = { x: 3, y: 4 };

console.log(p.getLength.call(obj)); // 5


function foo() {
    var args = [].slice.call(arguments);

    console.log(Array.isArray(arguments)); //false
    console.log(Array.isArray(args));  //true
}

foo()

apply

// apply

function __reverseArgs__(fn) {
    return function() {
        var args = [].slice.call(arguments);

        return fn.apply(this,args.reverse());
    }
}

function foo() {
    console.log(Array.from(arguments));
}

var foo2 = __reverseArgs__(foo);

foo2(1, 2, 3, 4);

call 与 bind


function add(x, y) {
    return x + y;
}

console.log(add.call(null, 1, 2)); //3
console.log(add.apply(null, [1,2])); //3 

console.log(add.bind(null, [1, 2])); // function

let add1 = add.bind(null, 1);//先传一个1 返回一个函数 等待第二个传递第二个参数传入

let add2 = add.bind(null, 1, 2); //如果是bind 返回一个函数 如果是call 立即执行
let add3 = add.call(null, 1, 2); //如果是call 立即执行


console.log(add1(2))  //  bind 是部分调用的函数

console.log(add2());
console.log(add3);


function setBodyState(state) {
    document.body.className = state;
}

setBodyState.call(null, 'state1');

setTimeout(setBodyState.bind(null, 'state2'), 1500)

// bind 支持异步绑定 call 是立即执行的

异步与回调函数

java 的异步

  • Timer
  • 事件
  • 获取数据API
  • Promise
  • 其它异步模型

用timer 异步执行

    <div id="ball">
    </div>

#ball {
    width: 50px;
    height: 50px;
    line-height: 50px;
    text-align: center;
    border-radius: 50%;
    border: 1px solid red;
    margin: 50px auto;
}

#ball:hover {
    cursor: pointer;
}

#ball.warn {
    animation: 1s 7s blink linear 3 forwards normal;
}

@keyframes blink {
    0% {
        background:#fff
    }
    50% {
        background: #f00
    }
    100% {
        background: #fff
    }
}



const ball = document.getElementById('ball');

ball.addEventListener('click', function() {
    var startTime = Date.now();

    var tId = setInterval(function() {
        var t = 10 - Math.round((Date.now() - startTime) / 1000);

        ball.innerHTML = Math.max(t, 0);

        if (t <= 0) clearInterval(tId);

        ball.className = 'warn';

    }, 1000)
});

动画专用

const brick = document.getElementById('brick');

brick.addEventListener('click', function() {
    let startTime = Date.now();

    let cycle = 2000;

    requestAnimationFrame(function update() {

        let p = (Date.now() - startTime) / cycle;
        brick.style.transform = 'rotate(' + 360 * p + 'deg)';

        requestAnimationFrame(update);
    })

})

Promise
MDN Promise

setTimeout(function() {
    console.log('step1')
    setTimeout(function() {
        console.log('step2')
        setTimeout(function() {
            console.log('step3')
        }, 1500)
    }, 1000)
}, 500)


function wait(time) {
    return new Promise(resolve => {
        setTimeout(resolve, time);
    })
}


wait(5000).then(function() {
    console.log('step4');

    return wait(1000);
}).then(function() {
    console.log('step5')
    return wait(1500);
}).then(function() {
    console.log('step6')
})

(async function() {
    await wait(5000);
    console.log('step7');
    await wait(1000);
    console.log('step8');
})()

其它异步模型

  • 异步串行模型 through2 –gulp 使用
  • 异步串行模型 connect – express koa 使用
  • ES6 generators
  • ES2015 async/wait

函数的动态创建

  • Function 构造器与函数模版
  • 过程抽象与高阶函数

用Function 处理不规范的JSON

// 处理不规范的json

   // 处理不规范的json

     var brokenJSON = `{
        a: 1,
        b:  2,
        c: 'message'
     }`

     function parseDate(data) {
         return (new Function('return' + data))();
     }

     try {
        console.log(JSON.parser(brokenJSON));
     }catch(ex){
      console.log(ex.message);
      console.log(parseDate(brokenJSON))
     }

     let add = new Function('x','y', 'return x+y');
     console.log(add(1, 2));

抽象过程与函数式编程

  • 过程抽象的定义
  • 纯函数
  • 等价函数
  • 举例说明
  • 过程抽象与函数式编程

过程抽象与数据抽象的区别

这里写图片描述

过程抽象与数据抽象的区别

define: f - > dataFromF = f(data)

define: g - > result = g(dataFromF)

// 数据抽象

define: f - > dataFromF = f(data);

define: g - > k = g(f), result = k(data);

// 过程抽象

纯函数

什么是纯函数?

一个函数如果输入参数确定, 如果输出结构是唯一确定的, 那么它就是纯函数

纯函数好处?

无状态 无副作用 幂等,无关时序

// 下面哪些是纯函数
// 第一个和最后一个是纯函数 其它都不是

function add(x, y) {
    return x + y;
}

function random(min, max) {
    return Math.floor(Math.random() * (max - min) + min);
}

let count = 0;

function addCount() {
    count++;
}

function setBgColor(color) {
    background.body.backgroundColor = color;
}

function __reduce__(fn) {
    return function(...args) {
        return args.reduce(a, b) => fn(a, b);
    }
}

过程抽象提升函数纯度

function setColor(el, color) {
    el.style.color = color;
}

function __multi__(fn) {
    return function(arrayLike, ...args) {
        return Array.from(arrayLike).map(item => fn(item, ...args));
    }
}



function setColors(els, color) {
    Array.from(els).map(el => setColor(el, color));
}

let setColors = __multi__(setColor);

setColors(document.querySelectorAll('#datalist > li'), 'red');

function add(x, y){
    return x + y;
}

add = __multi__(add);

console.log(add([1,2], 3)) // [4, 5] 

等价函数

    function __equal__(fn) {
        return function(...args){
           return fn.apply(this, args);
        }
    }

    function add(x, y) {
        return x + y;
    }

    add = __equal__(add);

    console.log(add(3,4)); //7

    let obj = {
        x: 1,
        y: 2,
        add: function(){
            return this.x + this.y
        }
    }

    let objAdd = __equal__(obj.add);

    console.log(objAdd.call(obj));  //3

拦截和监控

function __watch__(fn) {
    return function f(...args) {
        if (f.before) {
            f.before(this, args);
        }

        let ret = fn.apply(this, args);

        if (f.after) {
            f.after(this, ret, ...args);
        }
        return ret;
    }
}

$ = __watch__($);

$.after = function(thisObj, retVal) {
    if (retVal.css) {
        retVal.css = __watch__(retVal.css);
        retVal.css.before = function() {
            console.log("不推荐使用 .css 建议使用className")
        }
    }
}

let el = $('#datalist > li');

el.css('color', 'red');

性能优化

function __watch__(fn) {
    return function f(...args) {
        let blocked = false;
        if (f.before) {
            blocked = f.before(this, ...args) === true;
        }
        if (!blocked) {
            let ret = fn.apply(this, args);
            if (f.after) {
                f.after(this, ret, ...args);
            }
            return ret;
        }

    }
}

$ = __watch__($);

$.after = function(thisObj, retVal) {
    if (retVal.css) {
        let = _origin = retVal.css
        retVal.css = __watch__(retVal.css);
        retVal.css.before = function(target, ...agrs) {
            requestAnimationFrame(() => {
                // 用 requestAnimationFrame 优化性能
                _origin.apply(target, agrs);
            })

            // 返回 true 组织默认行为
            return true
        }
    }
}

let el = $('#datalist > li');

el.css('color', 'red');

假设 原始函数不支持 reduce


function add(x, y) {
    return x + y;
}

function mul(x, y) {
    return x * y;
}

function concat(arr1, arr2) {
    return arr1.concat(arr2);
}

console.log(add(1, add(2, 3))) //6

console.log(mul(1, mul(2, mul(3, 4)))) //24

console.log(concat([1, 2], concat([3, 4], [5, 6]))) //[1, 2, 3, 4, 5, 6]

方案1 改写三个方法

function add(...args) {
    return args.reduce((x, y) => x + y);
}

function mul(...args) {
    return args.reduce((x, y) => x * y);
}

function concat(...args) {
    return args.reduce((arr1, arr2) => arr1.concat(arr2));
}

console.log(add(1, 2, 3)) //6

console.log(mul(1, 2, 3, 4)) //24

console.log(concat([1, 2],[3, 4], [5, 6])) //[1, 2, 3, 4, 5, 6]

方案2:包装为reduce

function reduce(fn, ...args) {
    return args.reduce(fn);
}


function add(x, y) {
    return x + y;
}

function mul(x, y) {
    return x * y;
}

function concat(arr1, arr2) {
    return arr1.concat(arr2);
}

console.log(reduce(add, 1, 2, 3)) //6

console.log(reduce(mul, 1, 2, 3, 4)) //24

console.log(reduce(concat, [1, 2], [3, 4], [5, 6])) //[1, 2, 3, 4, 5, 6]

方案三 bind 一下

function reduce(fn, ...args) {
    return args.reduce(fn);
}


function add(x, y) {
    return x + y;
}

function mul(x, y) {
    return x * y;
}

function concat(arr1, arr2) {
    return arr1.concat(arr2);
}

add = reduce.bind(null, add);
mul = reduce.bind(null, mul);
concat = reduce.bind(null, concat)

console.log(add(1, 2, 3)) //6

console.log(mul(1, 2, 3, 4)) //24

console.log(concat([1, 2], [3, 4], [5, 6])) //[1, 2, 3, 4, 5, 6]

方案4 :函数变换(过程抽象)

function __reduce__(fn) {
    return function(...args){
        return args.reduce(fn.bind(this));
    }
}


function add(x, y) {
    return x + y;
}

function mul(x, y) {
    return x * y;
}

function concat(arr1, arr2) {
    return arr1.concat(arr2);
}

add = __reduce__(add);
mul = __reduce__(mul);
concat = __reduce__(concat);

console.log(add(1, 2, 3)) //6

console.log(mul(1, 2, 3, 4)) //24

console.log(concat([1, 2], [3, 4], [5, 6])) //[1, 2, 3, 4, 5, 6]

方案5 支持异步

function __reduce__(fn, async) {
    if (async) {
        return function(...args) {
            return args.reduce((a, b) => {
                return Promise.resolve(a).then((v) => fn.call(this, v, b));
            });
        }
    } else {
        return function(...args) {
            return args.reduce(fn.bind(this))
        }
    }

}


function add(x, y) {
    return x + y;
}

function mul(x, y) {
    return x * y;
}

function concat(arr1, arr2) {
    return arr1.concat(arr2);
}

function asyncAdd(x, y) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(`${x} + ${y} = ${x + y}`);
            resolve(x + y);
        }, 1000)
    });
}

add = __reduce__(add);
mul = __reduce__(mul);
concat = __reduce__(concat);

console.log(add(1, 2, 3)) //6

console.log(mul(1, 2, 3, 4)) //24

console.log(concat([1, 2], [3, 4], [5, 6])) //[1, 2, 3, 4, 5, 6]

asyncAdd = __reduce__(asyncAdd, true);

asyncAdd(1, 2, 3, 4, 5, 6).then((v) => console.log(v));

函数异步化和串行执行

function __reduce__(...fnList) {
    return function(...args) {
        if (fnList.length <= 0) return;

        fnList[0] = fnList[0].apply(this, args);

        return fnList.reduce((a, b) => b.call(this, a));
    }
}

function __pipe__(...fnList) {
    return function(...args) {
        var fn = fnList.reduceRight((a, b) => (...args) => b.apply(this, [...args, a]));

        return fn.apply(this, args);
    }
}

function add(x, y) {
    return x + y;
}

function double(x) {
    return 2 * x;
}

var foo = __reduce__(add, double, double, double);

console.log(foo(1, 2));

function taskA(x, next) {
    console.log(`task a: ${x}`);
    next()
}

function taskB(next) {
    console.log('task b');
    next()
}

function taskC() {
    console.log('task c')
}

var foo2 = __pipe__(taskA, taskB, taskC);
foo2(10);

throttle 避免重复点击 节流


const btn = document.getElementById('btn');

function throttle(fn, wait) {
    var timer;

    return function(...args) {
        if (!timer) {
            timer = setTimeout(() => timer = null, wait);

            return fn.apply(this, args);
        }
    }
}

// 按钮每500ms 一次点击有效

btn.onclick = throttle(function() {
    console.log('button clicked')
}, 500)

debounce 防止重复点击

const btn = document.getElementById('btn');

function debounce(fn, delay) {
    var timer = null;

    return function(...args) {
        clearTimeout(timer);
        timer = setTimeout(() => fn.apply(this, args), delay);

    }
}


btn.onclick = debounce(function() {
    console.log('button clicked')
}, 300)

multicast 批量操作DOM元素

function multicast(fn) {
    return function(list, ...args) {
        if (list && list.length != null) {
            return Array.from(list).map((item) => fn.apply(this, [item, ...args]));

        } else {
            return fn.apply(this, [list, ...args]);
        }
    }
}

function setColor(el, color) {
    return el.style.color = color;
}

setColor = multicast(setColor);

var list = document.querySelectorAll('li:nth-child(2n+1)');

setColor(list, 'red');

关于函数式编程的呢个(FP)

  • 什么式函数式编程
  • 函数式编程与前端代码有什么关系

    • 函数的纯度和”提纯”
    • Uncurrying Currying & Partial Application
  • 阅读

    • JavaScript 与函数式编程
    • 函数式编程术语解析
    • 什么式纯函数
    • 函数式编程离我们还有多远
    • 高阶函数对系统的”提纯”
      计算机程序的构造与解释

如何封装好的函数

  • 明确职责
  • 限制副作用
  • 过程的优化
  • 掌握抽象度

    总结
    这一课 我们学习了什么?

函数的本质式 :封装过程 开放接口
理解过程抽象和函数式编程

Tip: 好的程序设计方式式面向接口编程, 而不是面向实现编程

阅读

MDN JavaScript 指南, JavaScript 参考文档

作业练习

  • leetcode 刷题 :任意5道题目 记录解法心得
  • 阅读jQuery 源码 ,模仿实现jQuery 链式调用和批量操作的API
  • 前面的课程离讲过数据视图实现双向绑定的原理, 但没有封装,试着实现一个简单的数据视图双向绑定的封装
  • 继续优化和完善你们自己做小网站功能
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值