zoukankan      html  css  js  c++  java
  • [转]JavaScript函数和数组总结

    转自:http://www.uml.org.cn/AJAX/201307264.asp

    写的不错,对我有很多帮助,最近准备全面的学习研究一下ES5,先转载一下这篇文章。

    JavaScript函数

    1.函数的定义

    函数名称只能包含字母、数字、下划线或$,且不能以数字开头。定义时可用函数定义表达式或者函数声明语句。

    var f = function fact(x){}

    函数定义表达式包含名称,名称将作为函数的局部变量,在函数内部使用,代指函数。

    函数声明语句不是真正的语句,不能出现在循环、条件、try/catch/finally以及with语句中;声明语句置于在不会执行到的位置,仍可被整个作用域可访问,可在被定义代码之前使用。定义表达式的变量声明被提前了,但是对变量赋值不会提前,函数在被定义之前无法使用,否则调用时会出现错误:"TypeError: undefined is not a function"

    return语句没有一个与之相关的表达式,则它返回undefined值;如果一个函数不包含return语句,那它只执行函数体内语句,并返回undefined给调用者;没有返回值的函数,有时称为过程。

    2.函数执行

    函数执行的几种方式,当作函数、方法、构造函数、间接调用。如果构造函数没有形参,可以省略实参列表和圆括号,调用函数传实参有间隔时,可用null或undefined占位符替代空白的参数。

    构造函数返回值情况

    function MyClass(name) {
        this.name = name;
        return name;  // 构造函数的返回值?
    }
    var obj1 = new MyClass('foo'); // MyClass对象
    var obj2 = MyClass('foo');     // ‘foo’
    var obj3 = new MyClass({});    // {}
    var obj4 = MyClass({});        // {}

    3.实参对象

    实参对象是类数组对象,可用arguments.callee递归调用,如果你把参数命名为arguments,那么这个参数就会覆盖它原有的特殊变量。

    4.函数的行参和实参

    定义的函数括号内靠后面的形参没传入相应的实参,则默认值为undefined,有人利用这个,隐式定义函数内部的局部变量。函数传入参数的校验及抛出错误,函数中实参传入的是引用,函数内部对其操作,对外部是可见的。

    // 函数传参引用
    var oo = {x:1,y:2,get z(){    (----------- 这种写法,要研究下。)
        return 3;
    }}
    function fns(obj){
        obj.h = 4;
    }
    fns(oo);

    5.函数属性、方法

    函数也是对象,所以也有属性和方法,函数的length属性,函数形参的个数。

    apply方法的第二个参数可以是数组或类数组对象。

    bind方法是ES5中新增,将函数“绑定至”对象并传入一部分参数,传入的实参放在完整实参列表的左侧。

    中文注释是本人添上去的,这个例子考虑到bind返回的函数被当成构造函数使用情况。

    /**
     * ES5.0支持bind函数,ES3.0不支持bind,这里在ES3中模拟实现,能应用于大部分场景,
     * 如有问题,欢迎一起探讨
     * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
     */
    Function.prototype.bind || (Function.prototype.bind = function (that) {
        var target = this;
        // If IsCallable(func) is false, throw a TypeError exception.
        // 通过call、apply调用时,校验传入的上下文
        if (typeof target !== 'function') {
            throw new TypeError('Bind must be called on a function');
        }
        var boundArgs = slice.call(arguments, 1);
        function bound() {
            // 返回的bind函数被当构造函数
            if (this instanceof bound) {
                var self = createObject(target.prototype);
                var result = target.apply(
                    self,
                    boundArgs.concat(slice.call(arguments)));
                // Object(result) === result 判断调用返回是不是对象
                return Object(result) === result ? result : self;
            }
            // 返回的bind函数以一般函数形式调用
            else {
                return target.apply(
                    that,
                    boundArgs.concat(slice.call(arguments)));
            }
        }
        // NOTICE: The function.length is not writable.
        bound.length = Math.max(target.length - boundArgs.length, 0);
        return bound;
    });

    6.高阶函数

    如果函数作为参数或返回值使用时,就称为高阶函数。

    7.作为命名空间的函数

    函数命名空间暴露接口有以下几种方法:

    var mylib = (function (global) {
        function log(msg) {
            console.log(msg);
        }
        log1 = log;         // 法一:利用没有var的变量声明的默认行为,在log1成为全局变量(不推荐)
        global.log2 = log;  // 法二:直接在全局对象上添加log2属性,赋值为log函数(推荐)
        return {        // 法三:通过匿名函数返回值得到一系列接口函数集合对象,赋值给全局变量mylib(推荐)
            log: log
        };
    }(window));

    8.自更新函数

    function selfUpdate() {
        window.selfUpdate = function() {
            alert('second run!');
        };
        alert('first run!');
    }
    selfUpdate(); // first run!
    selfUpdate(); // second run!

    这种函数可以用于只运行一次的逻辑,在第一次运行之后就整个替换成一段新的逻辑。

    9.函数书写规范

    1) 函数参数定义的几种注释

    /*,...*/
    AP.reduce || (AP.reduce = function(fn /*, initial*/) {});
    getPropertyNames(o,/*optional*/a) // /*optional*/表示参数可选
    function arraycopy(/*array */ from,/*index*/from_start){};

    2) 其它

    // bad
    function a() {
        test();
        console.log('doing stuff..');
        //..other stuff..
        var name = getName();
        if (name === 'test') {
            return false;
        }
        return name;
    }
    
    // good
    function a() {
        var name = getName();// 定义变量放在前面
        test();
        console.log('doing stuff..');
        //..other stuff..
        if (name === 'test') {
            return false;
        }
        return name;
    }
    
    // bad
    function a() {
        var name = getName();
        if (!arguments.length) {
            return false;
        }
        return true;
    }
    
    // good
    function a() {
        if (!arguments.length) {// 参数校验放在前面
            return false;
        }
        var name = getName();
        return true;
    }

    10.具有记忆功能的函数

    /**
     *  记忆函数
     */
    function memorize(f) {
        var cache = {};
        return function () {
            var key = arguments.length + '' + Array.prototype.join.call(arguments, ',');
            if (!(key in cache))
                cache[key] = f.apply(null, arguments);
            return cache[key];
        }
    }

    JavaScript数组

    1.稀疏数组

    var a1 = [,,,];
    var a2 = new Array(3);
    var a3 = [1,2,3];
    
    console.log( 0 in a1);//false
    console.log( 0 in a2);//false
    console.log( 0 in a3);//true
    
    delete a3[0];
    console.log( 0 in a3);//false

    (这几个看着好神奇。)

    2.数组元素的增减

    length设置小于原值,删除溢出的;新增一数组元素,length始终元素序号+1。

    push/pop在数组尾部操作,与a[a.length]赋值一样;可支持多参数。

    unshift/shift在数组头部操作。

    3.数组遍历

    if(!a[i])       //null/undefined/不存在的元素
    if(a[i]===undefined)
    if(!(i in a))    //不存在的元素

    可用for in处理稀疏数组,不过数组不推荐用这个,比如能够枚举继承的属性名。

    4.数组的校验和类数组

    Array.isArray = Array.isArray || function(o){
        return typeof o ==="object" &&
            Object.prototype.toString.call(o) === "[object Array]";
    }

    类数组对象:可用针对真正数组遍历的代码来遍历;可用数组的通用方法。

    function isArrayLike(o){
        if(o &&
            typeof o ==='object' &&
            o.nodeType != 3 &&
            isFinite(o.length) &&
            o.length >= 0 &&
            o.length === Math.floor(o.length) &&
            o.length < 4294967296)
            return true;
        else
            return false;
    }

    5.作为数组的字符串

    在ES5(包括IE8)中,字符串行为类似只读数组。除了用charAt访问单个的字符外,还可以使用方括号。数组的通用方法可以用到字符串上。例如:

    s = "JavaScript"
    Array.prototype.join.call(s," ")    // => "J a v a S c r i p t"
    Array.prototype.filter.call(s,      // 过滤字符串中的字符
        function(x){
            return x.match(/[^aeiou]/); // 只匹配非元音字母
        }).join("");              // => "JvScrpt"

    注意,字符串是只读的,如果使用修改调用数组的方法,会报错。譬如:push()、sort()、reverse()、splice()等数组方法。

    6.ES3中数组支持的方法

    concat()—结果返回、sort() 、reverse() 、join()—结果返回、slice()—结果返回、splice()、push()、pop()、unshift()、shift()、toString()、toLocaleString()

    concat:返回一个新创建的数组,元素包含调用concat的原始数组的元素和concat的每个参数,如果这些参数中的任何一个自身是数组,则连接的数组的元素,而非数组本身。但要注意,concat不会递归扁平化数组的数组,也不会修改调用的数组。

    7.数组的slice和splice方法

    slice不改变原数组,截取原数组片段返回。索引从0开始,参数为正数时,第一个参数和不到第二个参数(从零算起,相对于索引)数组;参数为负数时,倒数第一个参数和倒数第二个参数的数组。

    splice改变原数组。第一个参数(从零算起),插入或删除的起始位置,第二个参数:删除元素的个数,第三或其它任意参数,要插入的元素。

    删除数组元素,保持数组连续,就用splice,返回的由删除元素组成的数组。复制数组的时候,请使用Array#slice。

    var len = items.length,
        itemsCopy = [],
        i; 
    
    // bad
    for (i = 0; i < len; i++) {
      itemsCopy[i] = items[i];
    }
    
    // good
    itemsCopy = items.slice();

    8.ES5中数组支持的方法

    1)forEach()

    var data = [1,2,3,4,5];
    data.forEach(function(v,i,a){
        a[i] = v + 1;
    });

    不能像for中使用break跳出循环,在forEach()传入参数的函数中抛出foreach.break异常。

    function foreach(a,f,t){  // a为数组,f为函数,t为函数f的执行环境
        try{
            a.forEach(f,t);
        } catch(e) {
            if( e === foreach.break)
                return;
            else
                throw e;
        }
    }
    foreach.break = new Error("StopIteration");

    在ES3不支持forEach()可引入以下方法:

    Array.prototype.forEach || (Array.prototype.forEach = function(fn, context) {
        for (var i = 0, len = this.length >>> 0; i < len; i++) {
            if (i in this) {
                fn.call(context, this[i], i, this);
            }
        }
    });

    2)map()

    a = [1,2,3];
    b = a.map(function(x){
        return x *x;
    });

    传递的参数跟forEach()一样,不同的是map()调用的函数要有返回值。该函数返回新创建的数组,不修改调用的数组。

    Array.prototype.map = Array.prototype.map || function (callback, thisArg) {
        var T, A, k;
        if (this == null) {
            throw new TypeError(" this is null or not defined");
        }
    
        // 1. 将O赋值为调用map方法的数组.
        var O = Object(this);
    
        // 2.将len赋值为数组O的长度.
        var len = O.length >>> 0;
    
        // 4.如果callback不是函数,则抛出TypeError异常.
        if ({}.toString.call(callback) != "[object Function]") {
            throw new TypeError(callback + " is not a function");
        }
    
        // 5. 如果参数thisArg有值,则将T赋值为thisArg;否则T为undefined.
        if (thisArg) {
            T = thisArg;
        }
    
        // 6. 创建新数组A,长度为原数组O长度len
        A = new Array(len);
    
        // 7. 将k赋值为0
        k = 0;
    
        // 8. 当 k < len 时,执行循环.
        while (k < len) {
            var kValue, mappedValue;
            //遍历O,k为原数组索引
            if (k in O) {
                //kValue为索引k对应的值.
                kValue = O[ k ];
                // 执行callback,this指向T,参数有三个.分别是kValue:值,k:索引,O:原数组.
                mappedValue = callback.call(T, kValue, k, O);
                // 返回值添加到新书组A中.
                A[ k ] = mappedValue;
            }
            // k自增1
            k++;
        }
    
        // 9. 返回新数组A
        return A;
    };

    3)filter()

    调用和forEach一样,返回的数组元素是调用的数组的一个子集。

    a = [5,4,3,2,1];
    
    smallvalues = a.filter(function(x){
        return x<3;
    });// [2,1]
    
    everyother = a.filter(function(x,i){
        return i%2 ==0;
    });// [5,3,1]
    
    // 跳过稀疏数组中缺少的元素,返回的数组总是稠密的
    var dense = sparse.filter(function(){
        return true;
    });
    
    // 压缩空缺并删除undefined和null元素
    a = a.filter(function(x){
        return x !==undefined x != null;
    })

    4)every()和some()

    对数组进行逻辑判定,返回true或false;注意every()和some()确定该返回什么值它们就会停止遍历数组元素。

    5)reduce()和reduceRight()

    使用指定的函数将数组元素进行组合,生成单个值。这在函数式编程中是常见的操作,称为“注入”和“折叠”。reduce需要两个参数,第一个参数是执行化简的函数,函数的参数分别为:化简操作累计的结果或初始化值、数组元素、元素的索引、数组的本身;第二个参数(可选)是传递给函数的初始值。

    var a = [1,2,3,4,5];
    var sum = a.reduce(function(x,y){
        return x + y;
    },0);
    var product = a.reduce(function(x,y){
        return x * y;
    },1);
    var max = a.reduce(function(x,y){
        return x>y?x:y;
    });

    reduceRight()的工作原理和reduce()一样,不同的是它按照数组索引从高到低(从右到左)处理数组。

    6)indexOf()和lastIndexOf()

    第一个参数代表要搜索的元素,第二个元素代表搜索的起始位置。可为负数,它代表相对数组末尾的偏移量,-1时,指定数组的最后一个元素。

    var a= [0,1,2,1,0];
    a.indexOf(1);   // => 1:a[1]是1
    a.lastIndexOf(1) // => 3:a[3]是1
    a.indexOf(3)    // =>-1: 没有值为3的元素
    
    function findAll(a,x){
        var result = [],
        len = a.length,
        pos = 0;
    
        while(pos<len){
            pos = a.indexOf(x,pos);
            if(pos === -1){
                break;
            }
            result.push(pos);
            pos++;
        }
        return result;
    }

    字符串也有indexOf()和lastIndexOf()方法。

  • 相关阅读:
    OSG-提示“error reading file e:1.jpg file not handled”
    OSG-加载地球文件报0x00000005错误,提示error reading file simple.earth file not handled
    QT-找开工程后,最上方提示the code model could not parse an included file, which might lead to incorrect code completion and highlighting, for example.
    我的书《Unity3D动作游戏开发实战》出版了
    java中无符号类型的第三方库jOOU
    Windows批处理备份mysql数据
    使用 DevTools 时,通用Mapper经常会出现 class x.x.A cannot be cast to x.x.A
    Java版本,Java版本MongoDB驱动,驱动与MongoDB数据库,Spring之间的兼容性
    Jrebel本地激活方法
    wget下载指定网站目录下的所有内容
  • 原文地址:https://www.cnblogs.com/ccforeverd/p/4143115.html
Copyright © 2011-2022 走看看