• JavaScript (JS) 函数补充 (含arguments、eval()、四种调用模式)


    1. 程序异常

    try-catch语法    测试异常

    try-catch语法代码如下: 

             try {

                 异常代码;     try中可以承重异常代码,

               console.log(“try”)  出现异常代码后,正确代码不会执行

    } catch (e) {

    console.log(“e:”+e);  try中出现异常在e中展现出来

                  console.log(“catch”);  只有try中出现异常才执行这段代码

    } finally {

    console.log( 1 );  无论try中有无异常,都会执行finally代码

    }

    throw语法    抛出异常

    throw(name: ”Tom”, age: 18)    抛出异常,抛出一个对象

    2. 函数的预解析

    代码如下:

        console.log(fn1);

        console.log(fn2);

        var fn1= function () {};

        function fn2(){}

    表达式式函数:     var fn1= function () {};    ; 结尾

    特点:没有函数名,预解析过程var fn1声明提升,”=”号的赋值没有执行,console.log(fn1)结果为undefined;

    声明式函数:       function fn2(){}      不用;结尾

    特点:有函数名(fn2),预解析过程已经将函数体赋值给了fn2,console.log(fn2)结果为function fn2(){}

    3. 作用域经典问题

        var  arr = [ { name: 'Tom' },

            { name: 'Jack' },

            { name: 'Jim' },

            { name: 'Lily' } ];

        for ( var i = 0; i < arr.length; i++) {

            arr[ i ].sayHello = function () {

                console.log( 'name = ' + arr[ i ].name );

            };

        }

        for ( var j = 0; j < arr.length; j++ ) {

            arr[ j ].sayHello();

    }

    代码执行会报错,因为当执行arr[j].sayHello()时,函数中arr[i].name才执行,此时i的值已经为4了,如果把代码改为:

        for ( var i = 0; i < arr.length; i++ ) {

            arr[ i ].sayHello();

    }

    则可以顺利执行,因为调用函数sayHello()时,对i进行了重新赋值,进入函数作用域内后arr[i]也就变为动态变化的值了

    该问题的由来,代码如下:

            <ul>

                     <li>①</li>

                     <li>② </li>

                     <li>③</li>

                     <li>④</li>

                     <li>⑤ </li>

            </ul>

            var list = document.getElementsByTagName( 'li' );

            var i;

            for ( i = 0; i < list.length; i++ ){

                     list[ i ].onclick = function () {

                              alert( list[ i ].innerHTML );   点击进入函数作用域时i的值已经为5

                alert( this.innerHTML );   list[i]改为this,可以正常执行

                     };

            }

    4.函数相关参数

    ① arguments

      arguments: 函数中默认的类似数组类型对象,里面存储了所有传入的参数

    ② 函数名.length

    函数中所有参数的个数,若无参数则为0;

    综合使用案例,封装一个extend扩展函数,代码如下:

        function extend() {       这种写法,只支持最多传入两个参数

            var args = arguments;

            if (args.length == 1) {

                for (var k in args[0]) {

                    this[k] = args[0][k];

                }

            } else {

                for (var k in args[1]) {        

                    args[0][k] = args[1][k];

                }

            }

        }

        var o1 = {name: "Tom"};

        var o2 = {age: 18, gender: "male"};

        var o3 = {goHome: "bus"};

        extend(o1, o2);     o2混入o1之中

        o3.extend = extend;

        o3.extend(o1);      o1添加到o3之中

    console.log(o3);

    ③ calleecaller

    1callee: 当前函数的引用

    语法:arguments.callee

        function foo() {

            console.log(arguments.callee === foo);   返回值为true

        }

    foo();

    callee一般在函数内部,实现函数递归时,使用callee表示函数引用

    2caller: 表示在fn1中调用fn2    兼容性不佳

    语法:函数名.caller   

        function fn1() {

            console.log(fn1.caller);      打印调用者fn2,打印结果如下:

    }                                                       function fn2() { fn1() };

        function fn2() {

            fn1();

        }

    fn2();

    5.eval(“ ”):动态执行函数

    eval(“var num=12;” )函数中的声明会在整个作用域起作用

    eval()Function()的比较

    eval:直接调用就执行eval(“alert(“执行”);”),代码执行

    Function:需要调用才执行,var fn = new Function(“alert(“执行”)”),这个过程是生成了一个函数,调用函数fn();代码执行,函数内声明只会在函数内起作用

    使用Function的立即执行函数(自调用函数):

    ( new Function(“alert(“执行”)” )) ();对函数使用 (函数)();括号结构相当于执行了函数

    任意函数的立即执行:

        (function fn() {

            alert("立即执行");

    })();

    json字符串转换成对象

    不严格的json格式(属性没有写双引号””)但js能够识别如下:

     var data='[{name:"Tom",age:18,gender:"male"},{name:"Jim",age:20,gender:"male"]';

    1)使用eval(); 传入json使用( )括起来

    var obj = eval(“(+data+)”);    转换后obj为json对象

    *使用eval()为何需要将data用圆括号括起来( ),变成表达式:

    a ) 标识符格式:任意名 + ,例如:label:

    标识符的使用:

        label:

                while (true) {

                    console.log("第一层循环");

                    while (true) {

                        console.log("第二层循环");

                        while (true) {

                            console.log("第三层循环");

                            break label;

                            console.log("第三层循环");

                        }                            

                        console.log("第二层循环");

                    }

                    console.log("第一层循环");

                }           

    执行结果如上图所示,使用标识符后,从第三层循环直接跳出所有循环

    b ) eval()中添加括号的解释:

        var s1 = '{ }';

    var o1 = eval(s1);

    console.log(o1)      没有任何结果undefined

        var s2 = '{name: "Tom"}';

    var o2 = eval(s2);

    console.log(o2)        输出Tom

        var s3 = '{name: "Tom" , age: 18}';

        var o3 = eval(s3);

        console.log(o1)       直接报错

    出现这种现象的原因是,eval( ){ }当做代码块来执行,o2中的name:被当做标识符所以输出”Tom”,而o3中的name:, age:被判定为用 ,号分割的标识符,是错误代码,所以直接报错,若将o3改为o3=eval(‘{name: “Tom” ; age: 18}’),使用; 号分割,则输出18,原因是系统默认age:标识符替换了name:标识符,所以最后解决这个问题的办法是用( )将代码括起来,变成表达式,再给eval( )解析

    2)使用Function

    var obj = (new Function(‘return’+data))();

    3)使用标准处理:JSON.parse()该方法必须使用严格的json格式,即属性必须写双引号””

    var data='[{"name":"Tom","age":18,"gender":"male"},{"name":"Jim","age":20,"gender":"male"]'

    var obj = JSON.parse(data);

    6.函数的四种调用模式

    函数调用模式  func()

    函数名+();   

        function func() {

            console.log(this);

        }

    func();

     

    函数是window   this指向window

    方法调用模式  obj.func()

    宿主对象obj调用函数,this指向引导方法的对象

        function func() {

            console.log(this);

        }

        func();

        var obj = {name: "object"};

        obj.func = func;

    obj.func();

     

    func():函数调用模式this指向window,obj.func():方法调用模式this指向obj

    构造器调用模式(constructor  使用new关键字引导

    new 是一个运算符,专门用来申请创建对象,创建出的对象传递给构造函数的this,利用构造函数对其初始化

    1)构造函数创建对象时的返回值(打印对象)

    如果构造函数没有设置参数,在创建对象时( )可以不写,例如:

    var obj = new Person这样写即可

    a ) 如果没有写返回值默认返回this

        function Person() {

            this.name = "Tom";

            this.age = 18;

            this.gender = "male";

        }

    var obj=new Person();

    console.log(obj);             Person {name: "Tom" , age: 18 , gender: "male"}

    b ) 如果返回值是基本数据类型numberstring则忽略返回类型,返回this

      function Person() {

            this.name = "Tom";

            this.age = 18;

            this.gender = "male";

            return "string"||123;       

        }

     var obj=new Person();

     console.log(obj);          Person {name: "Tom" , age: 18 , gender: "male"}

    c ) 如果返回值是引用数据类型,[ ]或者{ },则忽略this,返回该数据类型

      function Person() {

            this.name = "Tom";

            this.age = 18;

            this.gender = "male";

            return [ ] || { };  /  return { } || [ ]  

        }

        var obj=new Person();

        console.log(obj);      [ ]  /  { }

    2)综合分析题,代码如下:

        function Foo(){

            getName = function(){ alert(1); };

            return this;

        }

        Foo.getName = function(){ alert(2); };

        Foo.prototype.getName = function(){ alert(3); };

        var getName = function(){ alert(4); };

        function getName(){ alert(5); }

     

    Foo.getName();          

    2  方法调用,只有alert(2)是该方法调用

    getName();               

    4  函数声明提前,最后赋值为alert(4)

    Foo().getName();          

    1  Foo()函数调用,返回window,进入函数体,getName赋值为alert(1),且被window调用,等于函数调用模式

    getName();              

    1 函数调用模式,在上步已经赋值为alert(1)

    new Foo.getName();      

    2 创建Foo.getName()构造函数的对象,等于该方法调用alert(1)

    new Foo().getName();    

    3  new与()相结合,等于new Foo()构造函数创建的对象{}调用getName,由于构造函数没有定义getName属性,但是定义了Foo.prototype.getName,所以该对象{}继承了自己原型对象的getName属性alert(3)

    new new Foo().getName(); 

    3 同上步,new Foo()构造函数创建的对象{},所以该代码相当于new {}.getName(等于{}的方法调用模式创建对象,同理上一步,到Foo的原型对象找getName属性alert(3),这一步与上一步的区别是,上一步最后执行方法调用函数,这一步最后以上一步执行的方法调用函数为构造函数,创建对象

    上下文(环境)调用模式applycall

    1apply   参数以数组形式传入

    a ) apply只传一个对象的函数调用

        function fn() {

            console.log(this);

        }

        var obj = {name: "Tom", age: 18};

        fn();

        fn.apply(null);     apply中传入一个null或者不传参数    apply( ),等于函数调用模式

        obj.func =fn;

        obj.func();

        fn.apply(obj);    apply中传入一个对象,等于该对象的方法调用模式

    上面代码的运行结果为:

    fn()fn.apply(null)等价,obj.func()fn.apply(obj)等价

    b ) apply传入参数的函数调用   数组参数

    语法: function fn ( argu1 , argu2 ) { }; 函数

    fn.apply ( obj , [argu1 , argu2] )

    示例代码如下:

        function fn(num1, num2) {

            console.log(this);

            return num1 + num2;

        }

        var obj = {name: "Tom", age: 18};

        obj.func = fn;

        console.log(obj.func( 123 , 456 ) );

        console.log( fn.apply ( obj , [123, 456] ) );

    c ) apply使用案例

    一、 合并数组:

        var arr1 = [1, 2, 3];

        var arr2 = [4, 5];

        arr1.push(arr2[0],arr2[1]);

        [ ].push.apply(arr1,arr2);     这个方法与上面的方法等价

    二、 合并标签对象:

    <div>这是div标签1</div>

    <div>这是div标签2</div>

    <p>这是p标签1</p>

    <p>这是p标签2</p>

    将上述4个标签对象放入一个数组之中:

        var byTag =document.getElementsByTagName,arr=[ ];

        arr.push.apply(arr , byTag.apply(document, ["div"]));

        arr.push.apply(arr , byTag.apply(document, ["p"]));

        console.log(arr);

    由于getElementsByTagName( )是document的方法调用函数,所以过byTag获取该方法后,该方法必须在document对象下调用,所以在apply中传入document对象

    2call  参数以独立形势传入

    call用法与apply除了参数传入形势不同以外,其他基本相同

    语法: function fn ( argu1 , argu2) { };函数

    fn.call ( obj , argu1 , argu2 )

    借用构造方法创建对象:

        function Person(name, age, gender) {

            this.name = name;

            this.age = age;

            this.gender = gender;

        }

        function Student(name,age,gender,course) {

            Person.call(this, name, age, gender);

            this.course = course;

        }

        var student=new Student("Tom",20,"male","Math");

        console.log(student);

    7.补充知识

    bind调用  让函数绑定对象的一种方法,在调用函数时,就像是该对象在调用方法

    语法:函数.bind(对象),返回一个函数

    下列代码:

        var byTag =document.getElementsByTagName,arr=[ ];

        arr.push.apply(arr , byTag.apply(document, ["div"]));

        arr.push.apply(arr , byTag.apply(document, ["p"]));

        console.log(arr);

    使用bind对代码进行修改:

        var bindT = document.getElementsByTagName.bind(document), arr = [ ];

        arr.push.apply(arr, bindT("div"));

        arr.push.apply(arr, bindT("p"));

    console.log(arr);

    Object.prototype基本成员介绍

    1constructor

    2hasOwnProperty判断属性是否为自己提供

    console.log( obj.hasProperty(name”));判断obj是否具有name属性

    3propertyIsEnumerable  判断属性是否可以枚举

    4isPrototypeOf判断是否为原型对象

    console.log(fn.prototype.isPrototypeOf(obj)) 判断fn.prototype是obj的原型吗

    5toSting( ) , toLocaleString( ) , valueOf( )

    包装类型    基本类型数据本不应该包含方法调用

    基本类型数据,如数字,字符串在调用方法时,解释器先将基本类型数据转换成对应的对象类型,然后调用方法,执行方法后返回结果,同时销毁其转换的对象

    getset读写器

    语法糖:为方便开发而给出的语法结构

        var obj = (function () {

            var num = 123;

            return {

                get num() {

                    console.log("执行getter读写器");

                    return num;

                },

                set num(value) {

                    console.log("执行setter读写器");

                    num = value;

                    return num;

                }

            }

        })();

        console.log(obj.num);

        obj.num = 456;

        console.log(obj.num);

  • 相关阅读:
    Comparator
    Compare接口
    Predicate接口和Consumer接口
    Lambda表达式遍历集合
    Lambda表达式入门
    RansomNote
    FirstUniqueCharacterInString
    String All Methods
    形参个数可变的方法(...)
    springBoot excel导出 下载 超简单
  • 原文地址:https://www.cnblogs.com/Tabb/p/6440791.html
Copyright © 2020-2023  润新知