• 关于js函数的一些剖析 如bind call apply 原型链 作用域 作用域链 闭包的 函数提升的理解


    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
        </head>
        <body>
            <script type="text/javascript">
                // 函数作用域的例子理解
                console.log(aa(1)) // 1
                function aa(n) {
                    return n * n
                }
                // console.log(bb) // Cannot access 'bb' before initialization
                // console.log(bb(2)); // Cannot access 'bb' before initialization
                var bb = function(n) {
                    return n * n;
                }
                console.log(bb(2)) // 4
                // 2.函数提升仅适用于函数声明,而不适用于函数表达式 由上面的例子得出结论
                
                // 3.函数表达式也可以提供函数名,并且可以用于在函数内部代指其本身
                var cc = function fn(n) {
                    return n<2 ? 1 : n*fn(n-1)
                }
                console.log(cc(3)) // 6 fn(3) => 3*fn(2) => 3*2*fn(1) => 3*2*1 = 6
                
                // 嵌套的函数
                function getNum() {
                  var num1 = 6,
                      num2 = 8;
                  function add() {
                    return num1 + num2
                  }
                  return add();
                }
                // console.log(num1,num2) // num1 num2 is not defined =>得出 在函数内定义的变量不能在函数之外的任何地方访问
                console.log(getNum()) // 14 =>得出 在另一个函数中定义的函数也可以访问在其父函数中定义的 所有变量和父函数有权访问的任何其他变量
                
                var a = 2 , b = 4;
                function multiply() {
                  return a * b;
                }
                console.log(multiply()) // 8 => 得出 定义在全局域中的函数可以访问所有定义在全局域中的变量
                
                // 调用自身的函数我们称之为递归函数
                function nodeTree(node) {
                  if (node == null){
                     return;
                  }
                  // do something with node
                  for (var i = 0; i < node.childNodes.length; i++) {
                    nodeTree(node.childNodes[i]);
                  }
                }
                // 箭头函数  箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this
                // 箭头函数不能用作构造器,和 new一起用会抛出错误
                // 箭头函数没有prototype属性
                // yield 关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)
                var elements = [
                  'ssssss',
                  'ssssss',
                  'ssssss',
                  'ssssss'
                ];
                // 当箭头函数只有一个参数时,可以省略参数的圆括号
                const ele = elements.map(e => { 
                  return e.length; 
                }); 
                // 当箭头函数的函数体只有一个 `return` 语句时,可以省略 `return` 关键字和方法体的花括号
                const eles = elements.map(e => e.length); 
                console.log(ele,eles) // 返回数组 [6, 6, 6, 6] [6, 6, 6, 6]
                
                // call apply 调用函数的区别以及bind的作用:1.参数较少用call,参数较多用apply 2.传参方式不一样 call正常传  apply传数组
                function fn(a,b){
                    console.log(this) 
                    console.log(a+b)
                }
                //call调用函数,第一个参数用来改变this指向,除了第一个参数外的参数作为实参
                fn.call({name:'test-call'},10,20) // {name: "test-call"} 30
                // apply第二个参数为实参列表,调用会平铺开作为参数 apply传的是数组
                fn.apply({name:'test-apply'},[10,20]) // {name: "test-apply"} 30
                //作用:创建并返回一个新的函数,新函数跟fn函数长得一模一样,新函数的this指向被固定成了参数thisArg,并不会主动调用函数
                var newFn=fn.bind(this)
                console.log(newFn) // 返回 fn 场景,改变定时器中的this 重新绑定上下文 类似 var _this/that = this;这个操作
                
                // 构造函数的调用 这里扯到函数的调用分为 call、apply 、构造函数的调用以及函数普通的调用 constr()
                function constr(){
                    console.log(this,'111111') // constr {}  "111111"
                    this.a = 1
                }; 
                var so = new constr();
                console.log(so) // constr {a: 1}
                
                // 函数的getter和setter
                var newConstr = {
                    _name: '',
                    get name() { 
                        return this._name 
                    },
                    set name(newName) { 
                        this._name = newName 
                    }
                }
                // 测试 get set基本用法
                console.log(newConstr.name) // 输出 --> ''
                newConstr.name = 'set了一个值';
                console.log(newConstr.name) // 输出 --> set了一个值
                
                // Object.defineProperty的使用
                var testConstr = function() {
                    var _name = '';
                    var obj = {};
                    Object.defineProperty(obj, 'name', {
                        configurable: true,
                        enumerable: true,
                        get: function() {
                            return _name;
                        },
                        set: function(newName) {
                            _name = newName;
                        }
                    })
                    return obj;
                }();
                testConstr.name = "使用Object.defineProperty方法set了一个值";
                console.log(testConstr.name) // 输出 --> 使用Object.defineProperty方法set了一个值
                
                // 原型链
                // 每个实例对象( object )都有一个私有属性(称之为 __proto__ )指向它的构造函数的原型对象(prototype )。
                // 该原型对象也有一个自己的原型对象( __proto__ ) ,层层向上直到一个对象的原型对象为 null。
                // 根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
                let fun = function () {
                   console.log(this) // fun(){}
                   this.a = 1;
                   this.b = 2;
                }
                /* 这么写也一样
                function fun() {
                  this.a = 1;
                  this.b = 2;
                }
                */
                let ob = new fun();
                console.log(ob)  // {a: 1, b: 2}
                // 在fun函数的原型上定义属性
                // 不要在 fun 函数的原型上直接定义 fun.prototype = {b:3,c:4};这样会直接打破原型链
                fun.prototype.b = 3;
                fun.prototype.c = 4;
                console.log(ob.a,ob.b,ob.c,ob.d); // 1 2 4 undefined
                // 总结 (给对象设置属性会创建自有属性。获取和设置属性的唯一限制是内置 getter 或 setter 的属性)
                // 整个原型链如下: {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype---> null
                // a是ob的自身属性值为 1
                // b是ob的自身属性值为 2 原型上也有一个'b'属性,但是它不会被访问到,这种情况被称为"属性遮蔽"
                // c不是ob的自身属性吗 ==> 那看看它的原型上有没有 c是ob.[[Prototype]]的属性值为 4
                // d不是ob的自身属性 ==> 那看看它的原型上有没有 d不是 ob.[[Prototype]] 的属性 ==> 那看看它的原型上有没有 ob.[[Prototype]].[[Prototype]] 为 null,停止搜索 找不到d属性,返回undefined
                
                // 当继承的函数被调用时,this 指向的是当前继承的对象,而不是继承的函数所在的原型对象
                var foo = {
                  a: 2,
                  fb: function(){
                    return this.a + 1;
                  }
                };
                console.log(foo.fb()); // 3
                // 当调用 foo.fb 时,'this' 指向了 foo.
                var op = Object.create(foo); // op是一个继承自 foo 的对象
                console.log(op,'op') // {} 'op'
                op.a = 4; // 创建 op 的自身属性 'a'
                console.log(op,'op') // {a: 4}
                console.log(op.fb()); // 5
                // 调用 op.fb 时,'this' 指向了 op 又因为 op 继承了 foo 的 fb 函数 所以,此时的 'this.a' 即 op.a,就是 op 的自身属性 'a' 
                
                // 嵌套函数和闭包 
                // 内部函数只可以在外部函数中访问。内部函数包含外部函数的作用域
                // 内部函数形成了一个闭包:它可以访问外部函数的参数和变量,但是外部函数却不能使用它的参数和变量。
                function outfun(a) {
                  function infun(b) {
                    return a + b;
                  }
                  return infun;
                }
                let fnIn = outfun(3); // 可以这样想:给一个函数,使它的值加3
                res = fnIn(5);
                console.log(res,'res') // 8 "res"
                res1 = outfun(3)(5); 
                console.log(res1,'res1') // 8 "res1"
                
                // 多层嵌套函数,B和C都形成了闭包,所以B可以访问A,C可以访问B和A;闭包可以包含多个作用域;他们递归式的包含了所有包含它的函数作用域。这个称之为作用域链
                function A(a) {
                  function B(b) {
                    function C(c) {
                      console.log(a + b + c); // 输出6
                    }
                    C(3);
                  }
                  B(2);
                }
                A(1);
                
            </script>
        </body>
    </html>
  • 相关阅读:
    在纪念中国人民抗日战争暨世界反法西斯战争胜利70周年大会上的讲话
    ConcurrentHashMap 的实现原理
    聊聊并发(四)——深入分析ConcurrentHashMap
    Mybatis 动态 SQL
    Mybatis Mapper XML 文件
    MySQL的语句执行顺序
    Java 集合细节(二):asList 的缺陷
    java中 列表,集合,数组之间的转换
    将java中数组转换为ArrayList的方法实例(包括ArrayList转数组)
    把Java数组转换为List时的注意事项
  • 原文地址:https://www.cnblogs.com/lhl66/p/13063869.html
Copyright © 2020-2023  润新知