编程思考(一)

自进入前端以来,js权威指南这本书看过两遍,这一次结合codewars网站上js编程练习,温故知新。
这是一道在codewars上练习JS编程遇到的题目,个人觉得很经典,涉及到的知识点很多,因此就本题对涉及到的JS知识点做个相对全面的总结。

原题

function highAndLow(numbers){

}

Description:
In this little assignment you are given a string of space separated numbers, and have to return the highest and lowest number.
Example
highAndLow(“1 2 3 4 5”); // return “5 1”
highAndLow(“1 2 -3 4 5”); // return “5 -3”
highAndLow(“1 9 3 4 -5”); // return “9 -5”
Notes

  • All numbers are valid Int32, no need to validate them.
  • There will always be at least one number in the input string.
  • Output string must be two numbers separated by a single space, and
    highest number is first.

my solutions

function highAndLow(numbers){
  var b = numbers.split(" ");
  b.sort(function(a,b){
   return a-b;
  });
  var len = b.length;
  return b[len-1] + " " + b[0];
}

将字符串分割成数组,数组从小到大排序,输出数组的最后一个和第一个


easy solution

function highAndLow(numbers){
  numbers = numbers.split(' ').map(Number);
  return Math.max.apply(0, numbers) + ' ' + Math.min.apply(0, numbers);
}

字符串分割成数组,数组map方法,apply方法,Number中的Math.min和Math.max方法


比较以上两种接解决办法,相同点都用到了split方法将字符串转换成数组,不同点在于对转变后的数组进行操作,一种是直接排序,一种是将字符串数组转变为Number数组,从而调用Math.min和Math.max方法直接获得最值。


那么问题来了,在JS中数据类型分为原始类型(Number,String,Boolean,Null,Undefined)和对象类型,解题中运用到了字符串split方法,而字符串作为原始类型为什么会有方法呢?我们知道对象类型变量是可以有属性的,当属性时一个函数时可以称这个函数时该对象的方法。这里就涉及到了包装对象


下面就

  1. 包装对象
  2. 字符串方法(常用)
  3. 数组方法(常用)
  4. apply/call

展开讨论。


包装对象
JS中对象是一种复合值,它是属性或已有命名值的集合。通过“.”符号来引用属性值。当属性值是一个函数时,称其为方法。通过o.m来调用对象中的方法。

var blog = "this is my blog";
//使用字符串的属性
var word = blog.substring(blog.indexOf(" ")+1,blog.length)

字符串blog不是对象为什么会有属性呢?实际上只要引用了字符串blog的属性,JS就会调用new String(blog)将blog转换为对象,这个对象继承了字符串的方法,并被用来处理属性的引用,一旦引用结束,这个新创建的对象就会被销毁。
看下面代码的执行过程

var blog = "this is my blog";
blog.len = 4;
var t = blog.len;
console.log(t);

可以看到输出undefined,运行这段代码的时候,第二行代码会创建一个临时字符串对象。第三行通过原始的字符串值创建一个新的字符串对象,尝试读取len属性,失败!这说明了在读取字符串、数字、和布尔值的属性或方法的时候,这三种原始类型表现的像数据一样,但如果要设置给其设置新属性或者改变原来的属性值,会忽略这个操作,所有的修改只是发生在临时对象上,而这个对象被销毁了,并没有保存下来

//改变原属性值不生效
var s = "blog";
console.log(blog.length); //4
blog.length = 6;
console.log(blog.length)  //4
  • 在读取字符串,数字,布尔值的属性时创建的对象称作包装对象,用来区分字符串值/布尔值/数值和字符串对象/布尔值对象/数值对象

  • 字符串,布尔值,和数值的属性都是只读的,不能给他们定义新属性,有别于传统对象。

  • 可通过String(),Number(),Boolean()构造函数来显示创建包装对象。
var s = "test";
var S= new String(s);
s == S    //trtue
s=== S    //false
typeof s  //string
typeod S  //object

字符串方法
1. 字符方法 charAt( )|charCodeAt( )

var s = "hello world";
s.charAt(1)   //e
s.charCodeAt(1) //101小写字母e的字符编码

2.字符串操作方法 concat( )|slice( )|substring( )|sunstr( )

  • concat()将一个或多个字符串拼接起来,返回得到的新字符串,可接受任意多个参数。实践中“+”操作符更加简单!

      var s = "hello";
      var t = s.concat("world"); `
      console.log(s);     //hello
      console.log(t);     //helloworld
    
  • slice( )|substring( )|sunstr( )
    相同点:接受一个或两个参数,第一个参数指定字符串的开始位置,第二个参数表示字符串到哪里结束。如果没有给定第二个参数,则将字符串长度作为结束位置。
    不同点:slice()和substring()的第二个参数指定的是字符串最后一个字符后面的位置,而substr()第二个参数指定的是返回的字符的个数。

    var s = "hello world";
    s.slcie(3);          // "lo world"
    s.substring(3);      // "lo world"
    s.sunstr(3);         // "lo world"
    s.slcie(3,7);          // "lo w"
    s.substring(3,7);      // "lo w"
    s.sunstr(3,7);         // "lo worl"

    在传入负值的情况下:

    • slice()会将传入的负值和字符串的长度相加

    • substr()将负的第一个参数加上字符串的长度,负的第二个参数转换为0

    • substring()会将所有的负数都转化为0

    var s = "hello world";
    var len = s.length;       //11
    //slcie
    s.slice(-3);              //"rld"
    s.slice(-3,-1);           //"rl"
    slice(-3,-4);             // ""
    //substring
    s.substring(-3);          //"hello world"
    s.substring(-3,-1);       //""
    s.substring(-3,-4);       //""
    //substr
    s.substr(-3);              //"rld"
    s.substr(-3,-1);           //""
    s.substr(-3,-4);           //""

3.字符串位置方法 indexOf()|lastIndexOf()

indexOf()|lastIndexOf() 接受一个或两个参数
从字符串中查找子字符串的位置,如果没有找到该子字符串,则返回-1

```
//一个参数
var s = "hello world";
s.indexOf("o");       //4
s.lastIndexOf("o");    //7
//两个参数,第二个参数表示查找的初始位置
var s = "hello world";
s.indexOf("o",6);    //7
s.lastIndexOf("o",6) //4

```

4.trim()删除字符串前置及后置空格

```
var s= "  hello world  ";
var  trimmedS = s.trim();    //"hello world"
s === "hello world"          //true        
```

5.toUppeCase()|toLocaleUpperCase()|toLowerCase()|toLocaleLowerCase()
转换大小写
6. 字符串的模式匹配方法match()|search()|replace()

  • match()只接受一个参数:字符串|RegExp对象

    var text = "cat, bat, sat, fat";
    var pattern = /.at/;            
    var matches = text.match(patern);     //array[0]
    matches[0];                           //cat
  • search() 只接受唯一参数,字符串|RegExp()对象,方法返回字符串中第一个匹配项的索引

    var text ="cat, bat, sat, fat";
    var pos1 = text.search(/at/);
    console.log(pos1);          //1
    var pos2 = text.search(" ");
    console.log(pos2);          //4
  • replace() 接受两个参数,第一个参数可以使一个正则表达式或者一个字符串(这个字符串不会被转换成正则表达式),第二个参数可以是一个字符串或者一个函数。
    如果第一个参数是字符串,那么只会替换第一个子字符串。
    替换所有子字符串的方法是提供一个正则表达式,并指定全局标志(g)

    var text = "cat, bat, sat, fat";
    var result = text.replace("at","ond")
    console.log(result);   //"cond, bat, sat, fat"
    result = text.replace(/at/g,"ond");
    console.log(result);  //"cond, bond, sond, fond"

    7.split() 将字符串分割成数组,数组中的每个元素是string类型, 参数:分割字符串的分割符

数组方法
1. join() 将数组中所有元素都转化为字符串并连接在一起,返回最后生成的字符串,默认使用逗号和split()相反
不改变原数组

```
var a = [1,2,3];
a.join();         //"1,2,3"
a.join(" ");      //"1 2 3"
a.join("");       //"123"
console.log(a);   //[1,2,3]
var b = new Array(10);
b.join("-")      //"---------"9个连字号组成的字符串
```

2.reverse() 将数组元素颠倒顺序,返回逆序后的数组,原数组改变

```
var a = [1,2,3];
a.reverse().join();       //"3,2,1" a=>[3,2,1]
```

3.sort() 将数组中的元素排序,并返回排序后的数组,当不带参数调用sort()时,数组元素以字母表顺序排列,若数组包含undfined元素,则会被排列到数组的尾部。可以给sort()方法传递一个匿名函数,匿名函数确定排序规则。

```
var a = [33,4,1111,222];
a.sort();   //字母表顺序:1111,222,33,4
a.sort(function(a,b){
  return a-b;   //数值顺序,从小到大4,33,222,1111
});             //根据顺序返回负数,0,正数
```

4.concat(),创建并返回一个新数组,新数组的元素包括调用concat()的原始数组的元素和concat()的每个参数,如果这些参数中的任何一个自身是数组,则连接的是数组的元素,而非数组本身,但不会扁平化数组的数组,也不会修改调用的数组

var a = [1,2,3];
a.concat(4,5);        //[1,2,3,4,5]
a.concat([4,5]);      //[1,2,3,4,5]
a.concat([4,5],[6,7]); //[1,2,3,4,5,6,7]
a.concat(4,[5,[6,7]]);  //[1,2,3,4,5,[6,7]]

5.slice()方法,返回指定数组的一个片段或子数组,接受两个参数,分别指定片段开始和结束位置,返回的数组包含第一个参数位置到不包含第二个参数位置的所有数组元素。若参数出现负数,则表示相对最后一个元素的位置,-1最后一个元素,-4倒数第四个元素。
不改变原数组

var  a = [1,2,3,4,5];
a.slice(0,3);        //[1,2,3]
a.slice(3);          //[4,5]
a.slice(1,-1)        //[2,3,4]
a.slice(-3,-2)       //[3]

6.splice() 在数组中插入或删除元素的通用方法,会修改调用的数组。
splice能从数组中删除元素,插入元素到数组中,或者同时完成这两种操作,在插入或删除点之后的数组元素会根据需要增加或减小他们的缩影值,因此数组的其他部分仍然是保持连续的。
splice的第一个参数指定了插入和(或)删除的起始位置,第二个参数指定了应该从数组中删除的元素的个数,如果省略第二个参数,从起点开始到数组结尾的所有元素都将被删除,splice()返回一个由删除元素组成的数组,或者如果没有删除元素就会返回一个空数组。

var a = [1,2,3,4,5,6,7,8];
a.splice(4);    //返回[5,6,7,8],a是[1,2,3,4]
a.splice(1,2);  //返回[2,3],a是[1,4];
a.splice(1,1);  //返回[4],a是[1]

splice()前两个参数指定了需要删除的数组元素。紧随其后的任意个参数指定了需要插入到数组中的元素,从第一个参数指定的位置开始插入。

var a = [1,2,3,4,5];
a.splice(2,0,'a','b')  //[1,2,'a','b',3,4,5]

7.push() | pop()
pushpop允许将数组当作栈来使用,push()方法是在数组末尾添加一个或多个元素,并返回数组新长度。pop()删除数组的最后一个元素,并返回删除的值。
利用push()pop()可以对数组实现先进后出的栈。

var arr = []; 
arr.push(1,2);  //返回2      arr[1,2]
arr.pop();      //返回2      arr[1]
arr.push(3);    //返回2      arr[1,3]
arr.pop();      //返回3      arr[1]
arr.push([4,5]); //返回2     arr[1,[4,5]]
arr.pop();       //返回[4,5] arr[1]
arr.pop();       //返回1     arr[]

8.unshift() | shift()
unshift()在数组的头部添加一个或多个元素,并将已存在的元素移动到更高索引的位置来获取足够的空间,最后返回数组的新长度。shift()删除数组的第一个元素并将其返回,然后把所有随后的元素下移一个位置来填补数组头部的空缺。

var a = [];
a.unshift(1);    //a:[1] 返回1
a.unshift(22);   //a:[22,1] 返回2
a.shift();       //a:[1]   返回22

当使用多个参数调用unshift()时,参数是一次性插入的

var a = [1,3];
a.unshift(3,[4,5]); //a:[3,[4,5],1,3]

9.toString() | toLocalString()
数组和其他JS对象一样拥有toString()方法,针对数组,改方法将其每个元素转化为字符串,并且输出用逗号分隔的字符串列表,输出不包括方括号或其他任何形式的包裹数组的分隔符。
toLocalString()toString()的本地化

[1,2,3].toString();           //生成'1,2,3'
["a","b","c"].toString();     //'a,b,c'=>没有""
[1,[2,'c']].toString();        //生成'1,2,c'=>没有[]

ECMAScript5中的数组方法


概述
ECMASript 5定义了9个新的数组方法来遍历,映射,过滤,检测,简化和搜索数组。
大多数方法第一个参数接受一个函数,并且对数组的每个元素(或一些元素)调用一次该函数,如果是稀疏数组,对不存在的元素不调用传递的函数。
大多数情况下,函数使用三个参数,数组元素,元素的索引和数组本身,通常只需要第一个参数值,可以忽略后两个参数。
方法的第二个参数是可选的,如果有第二个参数,则调用的函数被看做是第二个参数的方法。在调用函数时传递进去的第二个参数作为它的this关键字来使用。被调用函数的返回值很重要。
ES5中数组方法不会修改原始数组,,方法中的函数时可以修改数组的。


1.forEach()
从头至尾遍历数组,为每个元素调用指定的函数。传递的函数作为forEach()的第一个参数,然后forEach()使用三个参数来调用该函数,数组元素,元素索引,数组本身。
可以编写只有一个参数的函数,其他参数忽略。

var data = [1,2,3,4,5];
var sum = 0;
data.forEach(function(value){
    sum+=value;
});               //sum=>15

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

注意:forEach()无法在所有元素都传递给调用的函数之前终止遍历。也就是说没有像for循环中使用相应的break语句,如果要提前终止,必须forEach()方法放在一个try块中,并能抛出一个异常。如果forEach()调用的foreach.break异常,循环会提前终止:

function foreach(a,f,t) {
    try{a.forEach(f,t);}
    catch(e) {
        if(e===foreach.break)return;
        else throw e;
    }
}
foreach.break = new Erroe("StopIteration");

2.map()
map()方法将调用数组的每个元素传递给指定的函数,并返回一个数组,它包含该函数的返回值。
map()返回的是新数组,不会修改调用的数组。

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

var arr = "1 2 3";
var b = arr.split(" ").map(Number);  //这里的函数是Number将字符转化为number类型
typeof  b[0];                //number

3.filter()
filter()方法返回的数组元素是调用的数组的一个子集。传递的函数是用来逻辑判定的;该函数返回true或者false。调用判定函数就像调用forEach()和map()一样。如果返回值为true或者能转化为true的值,那么传递给判定函数的元素就是这个子集的成员,它将被添加到一个作为返回值的数组中。

var 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]

//压缩空缺并删除undefined和null元素
a = a.filter(function(x){return x !== undefined && x != null;}); 

4.every()|some()
5.reduce()|reduceRight()
6.indexOf()|lastIndexOf()

call | apply
关于call和apply参考以下文章
https://www.zhihu.com/question/20289071 如何理解和熟练运用js中的call及apply?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值