JS中常用小技能

以下的内容均是在学习名为“守候”的博主在sementfault分享的文章,过程中自己有些地方没有理解到的和开始不懂的和容易错误的地方,通过调试理解后,记录在这里。附上该文章地址是https://segmentfault.com/a/1190000010225928,里面有许多实用的方法。

一、不可避免地用到的正则表达式
1.关于$1…9

看到$2的时候,惊觉自己看不懂。查找之后,对大家的描述始终不能理解,但是理解了之后,再回头去看大家的解释说得确实很对。就是这么纠结!
 把看到的小程序小小地改动了一下(哈哈,换了一首自己比较喜欢的现代诗),
              $ 1代表正则表达式匹配到的第一个子正则表达式;
              $2代表正则表达式匹配到的第二个子正则表达式;
              ...... 
  theObj.innerHTML=content.replace(Reg,"<span>$1</span>");//对匹配到的正则表达式,替换为第一个子表达式

效果如下图所示,要匹配的正则表达式为(你)(的),因此会首先找出所有的“你的”,再将“你的”替换为第一个正则表达式“你”,并设置字体颜色为红色。
这里写图片描述

这里写图片描述

接着将替换的内容换做$2:

theObj.innerHTML=content.replace(Reg,"<span>$2</span>");//对匹配到的正则表达式,替换为第一个子表达式

这里写图片描述

ok,验证结束!本例过程中,有对html标签进行过滤。原因是由于我们在选择到要选择的值时,为其增加了一个span标签,所以需要避免下一次操作时,上一轮的选择状态不会仍然存在。

//html标签的正则表达式,[^>]表示尖括号中间的内容
/<\/?[^>]*>/            

这里写图片描述

二. 数组迭代方法
1.map( ) :对数组中每一项遍历,可指定运行的函数,返回每次函数调用的结果所组成的数组。

这里写图片描述

2.filter( ):同理也是遍历数组,可给每一项指定运行函数,每次返回值是返回拿到值为true的项,因此最终返回是一个由返回项值为true的数组成员所组成的新数组。

这里写图片描述

3.every和some
(1) every( ):同理,遍历数组,且对于指定的函数,只有当每一项执行函数的返回值为true,才返回true;
(2)some( ):同理,遍历数组,却对于指定函数,只要任一项执行该函数时返回值为true,则最终函数返回值为true;
eg:
这里写图片描述
4.forEach( )
对数组中的每一项运行给定函数,这个方法没有返回值 ;

5.reduce( ) !!!每次只能接受两个参数
作用效果和原理就是[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4),累加计算

6.finde( )和findeIndex( )
(1)find( ):返回符合条件(条件也是在函数中调用)的数组第一个元素;
(2)findIndex( ):返回符合条件(条件也是在函数中调用)的数组第一个元素的位置索引(从0开始);
三、实用小方法
1.字符串大小写切换(源码详见原博主文章,地址见本文开始)
在使用replace( )方法时,第二个参数为匿名函数,且有多个参数。

switch (type) {
    case 1:
        return str.replace(/^(\w)(\w+)/, function (v, v1, v2, v3, v4) {
            console.log("v=",v);
            console.log("v1=",v1);
            console.log("v2=",v2);
            return v1.toUpperCase() + v2.toLowerCase();
        });
    case 2:
        return str.replace(/^(\w)(\w+)/, function (v, v1, v2, v3, v4) {
            console.log("vv=",v);
            console.log("v11=",v1);
            console.log("v22=",v2);
            console.log("v33=",v3);
            console.log("v44=",v4);
            return v1.toLowerCase() + v2.toUpperCase();
        });
    case 3:
        return ToggleCase(str);
    case 4:
        return str.toUpperCase();
    case 5:
        return str.toLowerCase();
    default:
        return str;
}       
var str = "aA_SS_AD*dM";
console.log(changeCase(str,1));
console.log(changeCase(str,2));

打印结果:
这里写图片描述

分析后,发现,第二个参数为函数时,其实有三个参数是被确定的:
(1)参数1:匹配到的字符串;
(2)倒数第二个参数:匹配到的字符串中的第一个字符在原字符串中的索引位置;
(3)最后一个参数:原字符串;

而中间可以有多个参数,分别代表匹配到的字符串中,第一个子表达式的内容、第二个、第三个…上面的例子中,第一个子表达式,表示包括下划线的所有字母的字符。
故:调用函数的第一种处理时(首字母大写),明显可以看到v是匹配到的字符串,v1是匹配字符串中第一个字表达匹配到的字符a,v2是…第二个子表达式匹配到的字符b,v3匹配第一个字符在原字符中的位置索引(从0开始),v4则为原表达达式!
!!!值得注意的是:
(1)当字符串后部分存在不识别的字符时,正则匹配时自动忽略后面,例如上面打印输出的部分,v只截取了之前的部分。
(2)只有通过正则表达式成功匹配时,才会执行后面的匿名函数,否则不会执行。验证如下:将字符串首字母改成
,因为匹配不成功,因此replace()方法返回值为原字符串本身。
这里写图片描述

2.快速将一个数组随机打乱
(1)sort()排序原理
查询后发现V8引擎 sort 函数只给出了两种排序 InsertionSort 和 QuickSort,数组长度小于等于 22 的用插入排序 InsertionSort,比22大的数组则使用快速排序 QuickSort。
这里附上两篇在csdn看到的比较清晰的关于插入排序和快速排序的两篇博文地址:插入排序算法详解及实现快速排序基本思路
经研究后发现插入排序算法,当需要交换时,没有看到具体的交换实现。只是将插入值位置处的值修改了(即待插入数组的第一个元素),而原来被插入位置上的值没有赋值等于插入值的过程???
所以,插入排序是怎样进行交换的?还有就是为什么第一轮比较的时候就直接交换成功了,a[2]没有重复a[1]的值
附上sor源码(V8):

function InnerArraySort(array, length, comparefn) {
  // In-place QuickSort algorithm.
  // For short (length <= 22) arrays, insertion sort is used for efficiency.
  //……
  //自我理解,from = 0;
  var InsertionSort = function InsertionSort(a, from, to) {
    for (var i = from + 1; i < to; i++) {
      var element = a[i];    //始终代表待插入数组的第一个元素
      for (var j = i - 1; j >= from; j--) {
        var tmp = a[j];     //顺次与从有序数组的最后一个元素开始相比较
        var order = comparefn(tmp, element);
        if (order > 0) {
          a[j + 1] = tmp;
        } else {
          break;
        }
      }
      a[j + 1] = element; //否则就不交换
    }
  };

//这部分未详细查看
  var QuickSort = function QuickSort(a, from, to) {
    var third_index = 0;
    while (true) {
      // Insertion sort is faster for short arrays.
      if (to - from <= 10) {
        InsertionSort(a, from, to);
        return;
      }
      if (to - from > 1000) {
        third_index = GetThirdIndex(a, from, to);
      } else {
        third_index = from + ((to - from) >> 1);
      }
      // Find a pivot as the median of first, last and middle element.
      var v0 = a[from];
      var v1 = a[to - 1];
      var v2 = a[third_index];
      var c01 = comparefn(v0, v1);
      if (c01 > 0) {
        // v1 < v0, so swap them.
        var tmp = v0;
        v0 = v1;
        v1 = tmp;
      } // v0 <= v1.
      var c02 = comparefn(v0, v2);
      if (c02 >= 0) {
        // v2 <= v0 <= v1.
        var tmp = v0;
        v0 = v2;
        v2 = v1;
        v1 = tmp;
      } else {
        // v0 <= v1 && v0 < v2
        var c12 = comparefn(v1, v2);
        if (c12 > 0) {
          // v0 <= v2 < v1
          var tmp = v1;
          v1 = v2;
          v2 = tmp;
        }
      }
      // v0 <= v1 <= v2
      a[from] = v0;
      a[to - 1] = v2;
      var pivot = v1;
      var low_end = from + 1;   // Upper bound of elements lower than pivot.
      var high_start = to - 1;  // Lower bound of elements greater than pivot.
      a[third_index] = a[low_end];
      a[low_end] = pivot;

      // From low_end to i are elements equal to pivot.
      // From i to high_start are elements that haven't been compared yet.
      partition: for (var i = low_end + 1; i < high_start; i++) {
        var element = a[i];
        var order = comparefn(element, pivot);
        if (order < 0) {
          a[i] = a[low_end];
          a[low_end] = element;
          low_end++;
        } else if (order > 0) {
          do {
            high_start--;
            if (high_start == i) break partition;
            var top_elem = a[high_start];
            order = comparefn(top_elem, pivot);
          } while (order > 0);
          a[i] = a[high_start];
          a[high_start] = element;
          if (order < 0) {
            element = a[i];
            a[i] = a[low_end];
            a[low_end] = element;
            low_end++;
          }
        }
      }
      if (to - high_start < low_end - from) {
        QuickSort(a, high_start, to);
        to = low_end;
      } else {
        QuickSort(a, from, low_end);
        from = high_start;
      }
    }
  };
  //……
}

(2)于是乎,利用sort( ) 方法随机打乱数组代码实现:

<script type="text/javascript">
var arr = [1,2,3,4,5,6,7,8,9,10];
    arr.sort(function(){
    	var d = Math.random();
    	console.log(arr);
    	console.log(d);
        return d - 0.5
    })
    console.log(arr);
</script>

输出打印值:
这里写图片描述
sort( )函数的语法及使用参见W3cschool
a.当return<0,a会被排到b前面就是,[a,b] ->[a,b];
b.当return=0,a和b的相对位置不变;
c.当return>0,b会被排到a的前面,[a,b]->[b,a];

3.最快捷的数组求最大值(下面用到的…运算符是es6中的扩展运算符)

var arr = [1,3,93,10];
Math.max(...arr);  //93
/*
  注:console.log(...arr); // 1 3 93 10 这个结果正是扩展运算符的作用
!!!疑问为什么Math.max(...arr)可以,而Math.max(1 3 93 10)不可以!中间经过了什么处理吗
*/
  1. 更短的数组去重方法
[...new Set([2,"12",2,12,1,2,1,6,12,13,6])]


四、Promise
学习期间总是免不了接触到Promise,每次见到时都是一脸懵逼态,终于找时间好好研究了一番,总算是拨开迷雾。
1.回调函数
首先了解一下回调函数,下面的代码是直接摘自博客园一博主的文章——JS回调函数–简单易懂有实例,在一片讲解中最是通俗易懂。正如验证结果显示的一样,主函数不用等回调函数执行完,就可接着执行自己的代码。

//定义主函数,回调函数作为参数
function A(callback) {
    callback();  
    console.log('我是主函数');      
}

//定义回调函数
function B(){
    setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作  
}

//调用主函数,将函数B传进去
A(B);

//输出结果
我是主函数
我是回调函数

关于回调函数应用挺大的,特别是在需要用到同步时尤为重要,像axios请求一般都是异步的,因此当我们后面的操作需要依赖前一个接口的返回数据时,由于异步执行,还没有等到返回,js已经执行后面的事件了,导致问题。

2.正式进行promise的理解(主要解决多层回调问题)
更详细讲解可以看“吕大豹”博主的文章——大白话讲解Promise(一),深入浅出,本人也是看完后才豁然开朗的。下面将其中的一些点简要记录一下,方便日后查看:
(1)将promise通过console.dir(将传入对象的属性,包括子对象的属性以列表形式输出)输出,看到Promise构造函数有多个方法如:resolve、reject等常见方法。

这里写图片描述
看到上面的图片相信仍旧不是那么直观,于是通过该博主中的小例子的实际效果来加强理解。

var p = new Promise(function(resolve, reject){
    //做一些异步操作
    setTimeout(function(){
        console.log('执行完成');
        resolve('随便什么数据');
    }, 2000);
});

Promise构造函数接收一个函数作为参数,其中的函数传入的两个参数分别是resolve和reject,当执行完承诺后,resolve令Promise的状态PromiseStatus = fulfilled,如果承诺完成失败,则reject()将PromiseStatus置为rejected。
!!!!!
虽然只是new了一个对象,没有调用,但仍旧会自动执行,打印输出“执行完成”。
因此Promise一般放在函数里,通过调用函数再运行它。
(2)Promise对象上有then、catch方法!我们可以通过下面的方法来获取执行成功后的resolve返回的数据,当执行完主函数后,就会打印输出resolve的参数。

function runAsync(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('执行完成');
            resolve('随便什么数据');
        }, 2000);
    });
    return p;            
}

//Promise方法,打印输出resolve中参数
 runAsync().then(function(data){
    console.log(data);
});

/

//回调函数法
function runAsync(callback){
    setTimeout(function(){
        console.log('执行完成');
        callback('随便什么数据');
    }, 2000);
}

runAsync(function(data){
    console.log(data);
});

如果单独的一两层回调还好,我们也可以定义一个callback,然后将定义的callback函数传递给主函数作为参数,也可实现同样功能。先执行主函数,2秒后打印输出“执行完成”,接着回调函数开始执行,打印输出“随便什么数据”。但是如果回调函数又是一个回调函数的话,那么函数就是层层嵌套,照如此思路还要再定义一个callback2给callback传入参数 _,故这就是Promise的优势之一,可以在then方法中继续写Promsie函数并返回,再次通过then()方法执行操作。详见博主原文,链式操作用法!

!!!!then( )方法不但可以返回Promise对象,也可以直接返回数据!

同理,当承诺执行失败时,then( )方法可以调用reject( )获取到失败原因。因此,可以添加then( )方法的第二个参数,该参数仍旧为一个函数,函数又有两个参数reason和data。当reject时,这个data貌似是获取不到的哟。

catch的用法:
同then( )的第二个参数一样,用于指定rejected回调,效果一样。
!!!另一个重要的作用,则是当抛出异常时,控制台报错,js工作会停止不往下运行了,但是这里就可以利用catch捕获到异常发生的原因,并且不报错。
(3)另外还有all( ) 和 race( )方法
all( ):前一个是按时间最短的为标准,所有异步操作函数均执行完成后才返回 ,返回的数据以数组形式传递给then( );
race( ):与上面相反,以最快的为标准。

这部分,结合前面提到的博主文章学习了解,记录了一下。算是对Promise有了入门理解,但难免有理解错误之处,欢迎指正。_

五、关于“对象”的理解
ECMAScript没有类的概念,对象区别于如Java、C++中的对象。

六、js中的函数继承
ECMAScript方法只支持实现继承(通过原型链),不支持接口继承。这是因为函数没有签名,签名百度说指函数名和他的参数列表(不包括返回值)。

七、闭包
1.立即执行函数(好处可以隔离作用域,不会破坏污染全局命名空间,因为匿名函数创建了一个新的函数作用域,访问全局对象或者变量是以参数形式传递进去的)
!!要想立即执行函数立即执行,函数体后面要有小括号且函数体必须是函数表达式而不能是函数声明!

立即执行函数两种常见形式:
(1)(function(){…})()

(2) (function(){…}())

因此,有如下的几种立即函数的小例子,通过使用()、+、-、!运算符都能讲匿名函数或者函数声明转换为函数表达式,起到立即执行作用。

(function(a){
   console.log(a);   //输出666,使用了()运算符
})(666);

!function(a){
   console.log(a);   //输出666,使用了!运算符将函数声明转换为了函数表达式
}(666);

+function(a){
   console.log(a);   //输出666,使用了+运算符将函数声明转换为了函数表达式
}(666);

-function(a){
   console.log(a);   //输出666,使用了-运算符将函数声明转换为了函数表达式
}(666);

var fn=function(a){
   console.log(a);   //输出666,使用了=运算符将函数声明转换为了函数表达式
}(666);

2 闭包与变量(理解不清楚,暂且记录着)
一直没有理解JavaScript高级程序设计中的这句话:“闭包只能取得包含函数中任何变量的最后一个值”
也正如书中那个典型的例子代码及效果图分别如下:

function createFunctions(){
	var result = new Array(); 
	for(var i=0; i<10; i++)
	{ 
		result[i] = function(){
			return i;
		}
	} 
	return result;
}
for(let i=0;i<10;i++){
  alert(createFunctions()[i]());
}

这里写图片描述

这里的createFunctions函数返回的result是一个函数数组,表面上看是的,似乎每个函数都返回对应的索引值,但实际上输出全部都是10。每个函数的作用域链中都保存着函数的活动对象,所以**它们引用的都是同一个变量i(早在书中闭包开始部分,作用链中执行变量对象的指针列表,仅仅只是引用实际上是不包含变量对象的),当createFunctions()函数返回后,i=10了,每个匿名函数中都引用着保存变量i的同一个变量对象,**占用的是同一块内存,因此随着变化,那个内存地址中的i会固定为最终的10。

随后书中对关于这个问题的解决方法则是,使用立即执行函数,将外部的i以参数形式传递给num函数参数的传递按值传递,因此将变量i的值复制给num。
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值