zoukankan      html  css  js  c++  java
  • 关于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>
  • 相关阅读:
    网络安全分析
    java实现 洛谷 P1464 Function
    java实现 洛谷 P1464 Function
    java实现 洛谷 P1014 Cantor表
    java实现 洛谷 P1014 Cantor表
    java实现 洛谷 P1014 Cantor表
    java实现 洛谷 P1014 Cantor表
    java实现 洛谷 P1014 Cantor表
    java实现 洛谷 P1540 机器
    java实现 洛谷 P1540 机器
  • 原文地址:https://www.cnblogs.com/lhl66/p/13063869.html
Copyright © 2011-2022 走看看