关于js函数你不知道的知识

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>关于js函数你不知道的知识</title>
    </head>
    <body>
        <script type="text/javascript">
            // 每个 JavaScript 函数实际上都是一个 Function 对象
            console.log((function(){}).constructor === Function) // true
            
            // Function 构造函数创建一个新的 Function 对象
            // Function 创建的函数只能在全局作用域中运行
            /*
                语法:new Function ([arg1[, arg2[, ...argN]],] functionBody)
                arg1, arg2, ... argN 参数名称是一个有效的JavaScript标识符的字符串,或者一个用逗号分隔的有效字符串的列表
                functionBody: 一个含有包括函数定义的 JavaScript 语句的字符串
            */
            const sum = new Function('x', 'y', 'return x + y');
            console.log(sum(2, 6)); // 8
            
            // 全局的 Function 对象没有自己的属性和方法,因为它本身也是一个函数,所以它也会通过原型链从自己的原型链 Function.prototype 上继承一些属性和方法
            console.log(new Function())
            
            // Function.prototype.constructor 声明函数的原型构造方法
            // Object.constructor 返回创建实例对象的 Object 构造函数的引用。
            // 注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串
            // 所有对象都会从它的原型上继承一个 constructor 属性
            var obj = {};
            console.log(obj.constructor === Object) // true
            
            var obj1 = new Object;
            console.log(obj1.constructor === Object) // true
            
            var arr = [];
            console.log(arr.constructor === Array) // true
            
            var arrs = new Array;
            console.log(arrs.constructor === Array) // true
            
            var num = new Number(1);
            console.log(num.constructor === Number) // true
            
            // 由 Function 构造器创建的函数不会创建当前环境的闭包,它们总是被创建于全局环境,
            // 因此在运行时它们只能访问全局变量和自己的局部变量,不能访问它们被 Function 构造器创建时所在的作用域的变量。
            var a = 10;
            function fn1() {
                var a = 20;
                return new Function('return a;'); // 这里的 a 指向最上面全局作用域内的 a
            }
            
            function fn2() {
                var a = 20;
                function f() {
                    return a; // 这里的 a 指向上方本地作用域内的 a
                }
                return f;
            }
            
            var f1 = fn1();
            console.log(f1());          // 10
            
            var f2 = fn2();
            console.log(f2());          // 20
            
            // Function.prototype.call() 在一个对象的上下文中应用另一个对象的方法;参数能够以列表形式传入
            /*
            语法 function.call(thisArg, arg1, arg2, ...)
                thisArg 可选的在function函数运行时使用的this值;
                arg1, arg2, ...指定的参数列表
            */
            function Product(name, price) {
              this.name = name;
              this.price = price;
            }
            
            function Fruits(name, price) {
              Product.call(this, name, price);
              this.category = 'fruits';
            }
            console.log(new Fruits('苹果', 15).name); 
            
            // 使用 call 方法调用函数并且不指定第一个参数(argument) 在严格模式下,this 的值将会是 undefined
            var str = 'wish';
            function fn() {
              console.log('str value is %s ', this.str);
            }
            fn.call();  // str value is wish 
            
            // Function.prototype.apply() 在一个对象的上下文中应用另一个对象的方法;参数能够以数组形式传入
            // call()方法的作用和 apply() 方法类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组
            /*
            语法 func.apply(thisArg, [argsArray])
                thisArg 必选的在func函数运行时使用的 this 值  
                argsArray 可选的一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数
            */
            const numList = [1, 2, 8, 6, 13];
            const max = Math.max.apply(null, numList);
            console.log(max,'最大值'); // 13 "最大值"
            const min = Math.min.apply(null, numList);
            console.log(min,'最小值'); // 1 "最小值"
            
            // 用 apply 将数组各项添加到另一个数组
            var arr1 = [1,2];
            var arr2 = [3,4,'a'];
            arr1.push.apply(arr1, arr2);
            console.log(arr1,'arr1') //  [1, 2, 3, 4, "a"] "arr1"
            
            // Function.prototype.bind() bind()方法会创建一个新函数,称为绑定函数.当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,
            // 传入 bind()方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数.
            function Counter() {
              // Math.ceil()  “向上取整”, 即小数部分直接舍去,并向正数部分进1
              this.count = Math.ceil(Math.random() * 12) + 1;
            }
            
            // 在 1 秒钟后声明 setNum
            Counter.prototype.setNum = function() {
              window.setTimeout(this.start.bind(this), 1000);
            }
            
            Counter.prototype.start = function() {
              console.log('延时一秒后打印:' + this.count + '!');
            }
            
            var num = new Counter();
            num.setNum();  // 一秒钟后, 调用start方法
            /*****************下面再来说说call、apply、bind的异同作用总结******************/
            /*
                apply的使用语法:函数名字.apply(对象,[参数1,参数2,...]);
                call的使用语法:函数名字.call(对象,参数1,参数2,...);
                bind的使用语法:函数名字.bind(对象,参数1,参数2,...);
                作用:前两个都是为了改变this指向,bind是改变this指向并且复制一个这个方法返回
            */
            function test(a,b,c){
                console.log(`this指向:${this},这里是参数和:${(a+b+c)}`);
            };
            test(1,2,3);// this指向:[object Window],这里是参数和:6
            let manObj = {
                name: "alai",
                age: 20
            };
            // 注意第一个区别,参数传递方式
            test.call(manObj,1,2,3); // this指向:[object Object],这里是参数和:6
            test.apply(manObj,[1,2,3]); // this指向:[object Object],这里是参数和:6
            test.bind(manObj,1,2,3); // 这里不输出,至于为什么我们来看三个函数的返回值
            
            // 查看返回值
            let aa = test.call(manObj,1,1,1);
            let bb = test.apply(manObj,[1,1,1]);
            let cc = test.bind(manObj,1,1,1);// 这里也不输出,要输出的话要调用cc();
            // 这里注意一点,bind使用可以先不传参数(除对象外的参数),调用返回值函数时再传
            cc();// this指向:[object Object],这里是参数和:3
            let dd = test.bind(manObj);
            dd(2,2,2);// this指向:[object Object],这里是参数和:6
            console.log(`调用aa:${aa}调用bb:${bb}调用cc:${cc}`);//调用aa:undefined调用bb:undefined调用cc:function () { [native code] }
            // 可以知道bind返回值是一个函数,call和apply没有返回值
            console.dir(test);
            // 可以看到test的系统原型"__proto__"中有这些方法,那么它指向哪里呢?
            // 因为函数就是对象,所以它可能的指向只有这两个
            console.log(test.__proto__ === Object.prototype); // false
            console.log(test.__proto__ === Function.prototype); // true
            // 结果不言而喻,这三个方法并不是方法的属性而是Function的原型下面的方法
            
            // 例子二 作用一:调用函数 作用二:call和apply可以改变函数中this的指向
            var strInfo = 'abcd';
            function bar(){
                // this指向window 
                var info = 'eghj';
                console.log(this.strInfo);  // abcd
                console.log(this === window)   // true
            }
            bar(); 
            bar.call();
            bar.apply(); 
            
            var newObj = {
                strInfo:'eeee'
            }
            bar.call(newObj); // 这里bar函数里面的this就指向了newObj 打印 eeee
            bar.apply(newObj); // 这里bar函数里面的this就指向了newObj 打印 eeee
            /*
            相同点:
                都能改变this的指向
                都能传递参数
                都能通过 方法"."方法名 调用
            不同点:
                函数名不同
                参数传递方式不同(call、bind正常传递,apply传递数组)
                改变this指向的时机不同(bind在复制时改变,其他两个在调用时改变)
                参数传递时机不同(bind使用可以先不传参数(除对象外的参数),调用返回值函数时再传,call、apply调用时候需要传递)
                手写call、apply、bind的实现参考本文:https://www.cnblogs.com/lhl66/p/13816988.html
            */
           
           // 给定 undefined 和 null 类型使用 Object  使用 Object 生成布尔对象
           var ob1 = new Object();
           console.log(ob1,'ob1') // {} "ob1"
           var ob2 = new Object(undefined);
           console.log(ob2,'ob2') // {} "ob2"
           var ob3 = new Object(null);
           console.log(ob3,'ob3') // {} "ob3"
           var ob4 = new Object(true); // 等价于 ob4 = new Boolean(true);
           console.log(ob4,'ob4') // Boolean {true}
           var ob5 = new Object(Boolean()); // 等价于 o = new Boolean(false);
           console.log(ob5,'ob5') // Boolean {false}
        </script>
    </body>
</html>

自己整理,请勿随意转载!!!!

posted @ 2021-03-05 18:42  蓝色帅-橙子哥  阅读(122)  评论(0编辑  收藏  举报