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);

原文地址:https://www.cnblogs.com/Tabb/p/6440791.html