Javascript(2):数组、函数基础加强

数组

数组是动态的、无类型的,数组元素可以为任意类型(表达式、数字、字符串、对象、数组…),并且同一个数组中的元素也可以为不同类型。元素下标范围:0~ 2 32 − 2 2^{32}-2 2322

数组的基本操作

数组创建

  1. 使用直接量 (注意:数组直接两的语法允许有可选的结尾逗号,所以[ , , ]数组中只有两个元素);
  2. 使用构造函数:
var a = new Array(); //[]空数组
var b = new Array(10); //单个数字表示预分配一个长度为10的数组,数组中的索引属性还未定义
var c = new Array(1, 2, 3, "test"); //创建一个元素为参数的数组

数组读写

所有的数组都是对象的特殊形式,数组就是对象所以JS将指定数字索引值转换为字符串,即1变为 “1”,以此作为属性名来使用。
所有的 索引都是属性名,但只有0~ 2 32 − 2 2^{32}-2 2322的属性名是索引。注意,可以用负数或非整数来索引数组, 这种情况下,数组转换为字符串,该字符串仅作为属性名来用,不能做索引。因为数组索引仅仅是对象属性的一种特殊类型,所以数组没有“越界”的错误概念,越界一律值为undefined。

a[-1.23] = true;    //创建了一个名为“-1.23”的属性
a["1000"] = 2;      //数组的第1001个数的值为2
a[1.000];           //和a[1]1相等

稀疏数组

是包含从0开始的不连续索引的数组。非稀疏数组即为稠密数组。


数组的length:是数组的一个属性,length属性使数组区别于常规的对象。稀疏数组的 length 大于元素的个数,稠密数组的 length 等于元素的个数。
针对 length的两个特殊行为:

  1. length随着索引最大值的增大而增大,而且必定大于数组的每一个索引值;
  2. 当 length小于该数组长度时,索引为大于等于 length的元素将从数组中删除;
 var a = [1, 2, 3, 4, 5];
 a.length = 0;      //删除了所有的元素,a为null
 a.length = 5;      //长度为5的空数组,等价于new Array(5)

如果数组中某个元素不能配置,就不能删除该元素,所以 length属性不能设置为小于等于该元素的索引值。


稀疏数组的创建:a.用Arraya( )构造函数 b.指定数组的索引值大于当前数组的长度 c.用delete操作符 d.省略数组直接量中的值时

a = new Array(5);
b = [];
b[20] = 2;
var c = [, , ,];  var d = [undefined];
console.log(0 in a);  //false
console.log(0 in b);  //false
console.log(0 in c);  //false
console.log(0 in d);  //true
delete d[1];
console.log(0 in d);  //false

数组遍历

  1. for循环
  2. for/in
    不存在的索引不会遍历到,但是for.in循环能够枚举继承的属性名,因此要进行过滤优化:
var a = [];
for(var i in a){
  if(a.hasOwnProperty(i)){    //跳过继承的属性
    continue;
  }
  if(String(Math.floor(Math.abs(Number(i))) == i){  //跳过不是非负整数的i
    continue;    //因为数组的索引会自动转为字符串,所以要先转换为Number,然后再转会String
  }
}
  1. forEach( ) 按照索引的顺序挨个传递给定义的一个函数
var a = [1, 2, 3, 4, 5];
var sum = 0;     //所以数据的平方和
a.forEach(function(x){  //把每个元素传递给此函数, 传的不是索引
  sum += x*x;
})
console.log(sum);

数组方法

  • join( )——将元素转化为字符串并连接在一起,连接符默认为",",可自选连接字符;String.split( )方法是该方法的逆向操作,不改变调用数组;
var a = [1, 2, 3, 4, 5];
var s = a.join("#");        //以#为连接符号
console.log(a.join("#"));   //1#2#3#4#5
var b = s.split("#");       //化为数组,注意:某些字符需要转义才能识别
console.log(b);             //['1', '2', '3', '4', '5']
  • reverse( )——将数组中的元素颠倒顺序;
  • sort( )——以字母顺序排序,undefined元素会被排到数组的尾部,元素值为undefined的正常排列;自定义非字母顺序进行数组排列,设定原理c++中一致:
var a = [33, 4, 1111, 222];
a.sort();
a.sort(function(a, b){
  return a-b;   //按照数字大小从小到大排列
})
  • concat( )——创建并返回一个新数组,数组元素为调用数组的元素和每个参数(若为数组则是数组中的元素),不改变调用数组本身,
  • slice( )——返回新数组不改变调用数组,元素为截取的数组片段。指定参数为数组索引,两个(左闭右开),一个(该处到数组末尾), 负数则逆向数;
  • splice( )——返回一个由删除元素组成的数组,没有删除就返回空数组;该方法实现删除、插入元素(若为数组则是插入该数组,不是数组中的元素),会改变调用函数;
var a = [1, 2, 3, 4, 5];
//第一个参数为操作起始索引,第二个参数为删除的元素个
//从第三个参数开始,如果后面跟的任意多个元素表示要插入的元素
console.log(a.splice(2, 0, ['a', 'b']));    //[] 未删除的元素
console.log(a);     //[ 1, 2, ['a', 'b'], 3, 4, 5]
  • push( ) and pop( )——用数组实现先进后出的栈。push( )在数组尾部添加一个或多个元素(若为数组则是插入该数组,不是数组中的元素),返回数组新的长度;pop( )在数组尾部删除最后一个元素返回删除的值。改变调用数组;

  • unshift( ) and shift( )——

  • toString( ) and toLocaleString( )
    二者的区别
    1.数字转换成字符串的时候,并没有感觉这两个方法有什么区别

2.当数字是四位及以上时,toLocaleString()会让数字三位三位一分隔,eg:1,234,456

3.如果是toLocaleString(),先判断是否指定语言环境(locale),指定的话则返回当前语言环境下的格式设置(options)的格式化字符串;没有指定语言环境(locale),则返回一个使用默认语言环境和格式设置(options)的格式化字符串。
如果是toString(),会直接返回标准的格式;

var sd=new Date();
sd 
//Wed Feb 15 2017 11:21:31 GMT+0800 (CST)
sd.toLocaleString()
//"2017/2/15 上午11:21:31"
sd.toString()
//"Wed Feb 15 2017 11:21:31 GMT+0800 (CST)"

数组类型

ES5以后用Array.isArray( )来判断某对象是否为数组;原理如下:

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

类数组对象

一种常常完全合理的看法把拥有一个数组length属性和对应非负整数属性的对象看做一种类型的数组。
eg:给常规对象增加length属性,然后遍历生成类数组对象:

var o = {};
while(i < 10){
	o[i] = i*i;
	i++;
}
o,length = i;

用来检测类数组对象的函数:

function isArrayLike(o){
	if(o && typeof o === "object" && 
	isFinite(o.length) &&             //检测o.length是否为有限制
	o.length>=0 && o.length===Math.floor(o.length) && o.length < 4294967296)
		return true;
	else
		return false;
}

JS数组方法特意定义为通用,所以不仅应用在真正的数组上也可以应用在类数组对象上,但类数组对象没有继承Array.prototype,不能直接调用数组方法,可以采用Function.call( )方法调用:

var a = {"0":"b", "1":"a", "2":"c", length:3};
Array.prototype.join.call(a,"+");

函数

函数表达式:var square = function(x) {…}
函数声明式:function square(x) {…}
两种方式函数名实际都是看不到,square仅仅是变量的名字,指代函数对象,将改变量赋值给其他变量仍可以正常工作;

函数调用

  1. 作为方法
    一个方法是保存在一个对象的属性里的JavaScript函数,方法调用和函数调用有一个重要的区别,即上下文this,此处对象成为调用上下文。任何函数作为方法调用都会传入一个隐式的实参——对象。

关于嵌套函数中的this:
嵌套的函数不继承调用它的函数中的this。如果嵌套函数作为方法调用,其this的直指向调用的对象;如果嵌套函数作为函数调用,其this非严格模式下指向全局对象,严格模式下是undefined。

var o = {
  m: function(){
    var self = this;
    console.log(this === o);    //true
    f();
    function f(){
      console.log(this);        //全局对象
      console.log(self === o);  //true,如果想要访问这个外部函数this值,只能将this的值存在变量里传入
    }
  }
}
o.m();
  1. 作为构造函数
    凡是没有形参得构造函数调用均可以省略圆括号,构造函数被调用来创建一个新的空对象,这个新对象将继承自构造函数得prototype属性。
 构造函数试图初始化这个新对象,并将这个对象作为其上下文
 因此构造函数可以用this来引用这个新创建得对象,此时o.m()this就是obj
var obj = new o.m();
注意:如果构造函数没有returnreturn为空,默认返回新对象,有return值自动忽略返回新对象
  1. 间接调用
    JS中的函数也是对象,任何函数可以作为任何对象的方法来调用,哪怕这个函数不是那个对象的方法。比如call( )和apply( )都可以间接地调用函数。

函数的实参和形参

  1. 可选形参(省略的形参将被赋值为undefined)
function getNames(o, /*optional*/ a){   //代码的可读性
	if(a === undefined) a = [];         //检测是否传入该参数,未传入就新建一a[]返回
	for(var property in o){
		a.push(property);         
	}
	return a;
}

将对象作为实参可以避免参数过多难以记住参数顺序的情况;


  1. 可变长的实参列表:实参对象,可以让函数操作任意数量的实参
    实参对象是一个类数组对象,对象中的属性(数组的元素)是传入的实参,标识符arguments是指向实参对象的引用,可以通过该标识符的数字下标访问传入函数的实参值,而不一定要通过名字获取实参;
//与内置函数MAX.max()功能类似
function max(/* ... */){        //代码规范,表明传入参数的数量不定
	var max = Number.NEGATIVE_INFINITY;
	for(var i = 0; i<arguments.length; i++){
		arguments[0] = 15;       //修改实参数组的元素同样会修改x的值,因为二者指向的是同一个值
		if(arguments[i] > max){
      		max = arguments[i];
   		 } 
 	 }	
  console.log(arguments.length);  //6 //类数组对象属性
  console.log(x);                 //1
  return max;
}
var largest = max(1,5,9,13,2,9);   //15

实参对象的callee和caller属性

自定义函数属性

和类数组对象相似,**函数作为一种特殊的对象,可以拥有属性,且和对象调用属性方法相同。**当函数需要一个“静态”变量来保持值不变时,可以给函数定义属性,避免使用会使命名空间变得杂乱的全局变量。
如计算阶乘的函数factorial( ):

function factorial(n){
  if(isFinite(n) && n>0 && n==Math.round(n)){ //判断是否为整数
    if(!(n in factorial)){                    //如果没有缓存结果,即没有这个属性
      factorial[n] = n*factorial(n-1);        //计算结果并缓存,即该函数增加了n这个属性
           //此处不能是factorial[n-1],因为可能也不存在这个属性,所以用递归求取
    }
    return factorial[n];                       //返回缓存结果
  }
  return NaN;
}
factorial[1] = 1;

续写中…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值