[JS] ECMAScript 6 - String, Number, Function : compare with c#

 

 

 

字符串的扩展 

jsjavascript

 

Ref: 模板字符串php

策略:${var},放在反引号中!html

经过tag来表示字符串。java

 使用for输出完整的字符串,记得最后一个strings[strings.length-1]。git

 

c#es6

Ref: 一个很是好的C#字符串操做处理类StringHelper.csgithub

Ref: 字符串(C# 编程指南)正则表达式

Ref: 经常使用C#字符串函数大全express

 

 

正则表达式的扩展

js编程

修饰符:

    • i(intensity)  :大小写不敏感。
    • g(global)    :全局查找,对于一些特定的函数,将迭代完整的字符串,得到全部的匹配结果,而不只仅在获得第一个匹配后就中止进行。
    • m(multiple):检测字符串中的换行符,主要是影响字符串开始标识符^和结束标识符$的使用。
var urlReg     = /(\w+):\/\/([\w.]+)\/(\S*)/;
var myHomepage = "http://www.wanjilong.com/homepage";
var result     = myHomepage.match(urlReg);
console.log(result);
for (var i = 0, len = result.length; i < len; ++i) { console.log(i, result[i]); }

  

Ref: JavaScript 正则表达式

Ref: JavaScript RegExp 对象

var patt = new RegExp(pattern,modifiers);

或者,更简单的方法

var patt = /pattern/modifiers;

test():一个字符串是否匹配某个模式

exec():检索字符串中的正则表达式的匹配

Method Description
exec RegExp method that executes a search for a match in a string. It returns an array of information or null on a mismatch.
test RegExp method that tests for a match in a string. It returns true or false.
match String method that executes a search for a match in a string. It returns an array of information or null on a mismatch.
search String method that tests for a match in a string. It returns the index of the match, or -1 if the search fails.
replace String method that executes a search for a match in a string, and replaces the matched substring with a replacement substring.
split String method that uses a regular expression or a fixed string to break a string into an array of substrings.

 

c#

Ref: C# 正则表达式

匹配:匹配了以 'm' 开头以 'e' 结尾的单词

using System;
using System.Text.RegularExpressions;

namespace RegExApplication
{
   class Program
   {
      private static void showMatch(string text, string expr)
      {
         Console.WriteLine("The Expression: " + expr);
         MatchCollection mc = Regex.Matches(text, expr);
         foreach (Match m in mc)
         {
            Console.WriteLine(m);
         }
      }
      static void Main(string[] args)
      {
         string str = "make maze and manage to measure it";

         Console.WriteLine("Matching words start with 'm' and ends with 'e':");
         showMatch(str, @"\bm\S*e\b");
         Console.ReadKey();
      }
   }
}

替换:替换掉多余的空格

using System;
using System.Text.RegularExpressions;

namespace RegExApplication
{
   class Program
   {
      static void Main(string[] args)
      {
         string input       = "Hello   World   ";
         string pattern     = "\\s+";
         string replacement = " ";
         Regex rgx = new Regex(pattern);
         string result = rgx.Replace(input, replacement);

         Console.WriteLine("Original String: {0}", input);
         Console.WriteLine("Replacement String: {0}", result);    
         Console.ReadKey();
      }
   }
}

 

 

数值的扩展

js

 

c#

Ref: C#数学计算包 Math.NET

Ref: Math Class【MSDN】

 

 

函数的扩展

js

  • 函数的 length 属性
(function (a) {}).length            // 1
(function (a = 5) {}).length        // 0
(function (a, b, c = 5) {}).length  // 2

 

指定了默认值后,length属性将失真。

后文的 rest 参数也不会计入length属性

(function(...args) {}).length // 0

/**
* 只计算最后一个有默认值参数以前的”未默认初始化“的参数的个数
*/ (
function (a = 0, b, c) {}).length // 0 (function (a, b = 1, c) {}).length // 1

  

  • 做用域

例一

let x = 1;

function f(y = x) {   # 调用函数时,参数造成一个单独的做用域
  let x = 2;          # 这一条语句 其实没用
  console.log(y);
}

f() // 1f

注意:参数中默认为是let,故锁住了做用域

例二

let foo = 'outer';

function bar( func = () => foo ) {  // 同理,foo是外层的
  let foo = 'inner';
  console.log(func());
}

bar(); // outer

 

例三:这里是三个做用域的x。

var x = 1;
function foo( x, y = function() { x = 2; } ) {  // 参数中的x是单独的做用域 var x = 3; // 函数内部的x是另外一个做用域   y(); // 执行后,内部变量和外部全局变量的值都没变 console.log(x); } foo() // 3 x // 1yxx

这里,只有两个做用域,函数参数和函数内部的做用域是同样的。

var x = 1;
function foo(x, y = function() { x = 2; }) {
  x = 3;                                        // 内部变量就指向第一个参数
  y();
  console.log(x);
}

foo() // 2
x // 1xx

  

  • 应用

至关棒的tricky的方法:利用参数默认值,能够指定某一个参数不得省略,若是省略就抛出一个错误。

一个不能够省略的参数:

一个能够省略的参数:

function foo(optional = undefined) { ··· }

 

 

  • rest 参数

比过去的argument要好

// arguments变量的写法
function sortNumbers() {
  return Array.prototype.slice.call(arguments).sort();
}

对象不是数组,而是一个相似数组的对象。
// 因此为了使用数组的方法,必须使用先将其转为数组。

-----------------------------------------------------------------------
// rest参数的写法 const sortNumbers = (...numbers) => numbers.sort();

// rest 参数就不存在这个问题,它就是一个真正的数组,数组特有的方法均可以使用。// argumentsArray.prototype.slice.call

 

rest 参数必须在尾部

rest 参数以后不能再有其余参数(即只能是最后一个参数),不然会报错。

// 数组参数的典型用法

function
push(array, ...items) { items.forEach(function(item) { array.push(item); console.log(item); }); } var a = []; push(a, 1, 2, 3)

 

  • 严格模式

只要参数使用了默认值、解构赋值、或者扩展运算符,就不能显式指定严格模式。

产生了矛盾点:"只有从函数体之中,才能知道参数是否应该以严格模式执行,可是参数却应该先于函数体执行"

// 报错
function doSomething(value = 070) {
  'use strict';  # 在执行这里前,如何处理参数是个问题 return value; }

那干脆禁止掉就行了!

 

但并非说,这跟‘严格模式’的初衷有冲突,两种方法能够规避这种限制。

第一种,是设定全局性的严格模式,这是合法的。

'use strict';

function doSomething(a, b = a) {
  // code
}

第二种,是把函数包在一个无参数的当即执行函数里面。

const doSomething = (function () {
  'use strict';
  return function(value = 42) {
    return value;
  };
}());

参考:[JS] Why "strict mode" here

 

  • name 属性
const bar = function baz() {};

// ES5
bar.name // "baz"

// ES6
bar.name // "baz"

其余状况见原文。

 

  • 箭头函数

一样的,注意大括号(代码块) 是否做为了返回值。

// 报错
let getTempItem = id => { id: id, name: "Temp" };

// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });

// 虽然能够运行,但会获得错误的结果
// 因为引擎认为大括号是代码块,因此执行了一行语句a: 1

// 这时,a能够被解释为语句的标签,所以实际执行的语句是1;
// 而后函数就结束了,没有返回值。
let foo = () => { a: 1 };
a: 1a1;foo() // undefined

// 若是箭头函数只有一行语句,且不须要返回值,能够采用下面的写法,
// 就不用写大括号了
let fn = () => void doesNotReturn();

 

便捷一:箭头函数能够与变量解构结合使用。

const full = ({ first, last }) => first + ' ' + last;

// 等同于 function full(person) { return person.first + ' ' + person.last; }

便捷二:简化回调函数

// 正常函数写法
[1,2,3].map(function (x) {
  return x * x;
});

// 箭头函数写法
[1,2,3].map(x => x * x);

便捷三:与 rest 参数相结合。

const numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5)
// [1,2,3,4,5]

const headAndTail = (head, ...tail) => [head, tail]; headAndTail(1, 2, 3, 4, 5) // [1,[2,3,4,5]]

 

箭头函数的使用注意点

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。 

this对象的指向是可变的;

可是在箭头函数中,它是固定的。

做用一

例子一:

function foo() {
  setTimeout( () => { console.log('id:', this.id);   }, 100 );
}

var id = 21;
// 对象{id:42}做为了参数
foo.call({ id: 42 });
// id: 42

例子二:

Timer函数内部设置了两个定时器,分别使用了箭头函数和普通函数。

前者的this绑定定义时所在的做用域(即Timer函数),

后者的this指向运行时所在的做用域(即全局对象)。

因此,3100 毫秒以后,timer.s1被更新了 3 次,而timer.s2一次都没更新。

 

做用二

箭头函数可让this指向固定化,这种特性颇有利于封装回调函数

注意:

this指向的固定化,并非由于箭头函数内部有绑定this的机制,实际缘由是箭头函数根本没有本身的this

致使内部的this就是外层代码块的this。

除了this,如下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量:argumentssupernew.target

// ES6
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

// ES5
function foo() {
  var _this = this;

  setTimeout(function () {
    console.log('id:', _this.id);
  }, 100);
}

请问下面的代码之中有几个this

 

 

(2)不能够看成构造函数,也就是说,不可使用new命令,不然会抛出一个错误。 

(3)能够用 rest 参数,但不可使用arguments对象,该对象在函数体内不存在。

(4)不可使用yield命令,所以箭头函数不能用做 Generator 函数。

 

  • 嵌套的箭头函数

以下,可见箭头函数带来的好处,有点builder or pipeline的感受。

function insert(value) {
  return { into: function (array) {
    return { after: function (afterValue) {
      array.splice(array.indexOf(afterValue) + 1, 0, value);
      return array;
    }};
  }};
}

insert(2).into([1, 3]).after(1); //[1, 2, 3]

上面这个函数,可使用箭头函数改写。【记得加圆括号

可读性提高,也能够采用下面的写法

const plus1 = a => a + 1;
const mult2 = a => a * 2;

mult2(plus1(5))
// 12

 

箭头函数还有一个功能,就是能够很方便地改写 λ 演算。

λ 演算】就是一种特殊的语法所书写的匿名函数。

参见:神奇的λ演算

 

  • call, apply

在原生js中会有三个很常见的函数,call, apply, bind。他们的做用就是改变当前函数的this指针。

Ref: 如何理解和熟练运用js中的call及apply?

-- 理论 --

当一个object没有某个方法,可是其余的有,咱们能够借助call或apply用其它对象的方法来操做!

另一个对象whiteDog = {food:"bone"},

咱们不想对它从新定义say方法,咱们能够经过call或apply用blackCat的say方法:blackCat.say.call(whiteDog)。

此时,say:function()中的this指针就成了whiteDog,this.food就变成了“bone”。

-- 实战 --

用的比较多的,经过document.getElementsByTagName选择的dom 节点是一种相似array的array。
它不能应用Array下的push,pop等方法。咱们能够经过:
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
这样domNodes就能够应用Array下的全部方法了。


  • 双冒号运算符

箭头函数能够绑定this对象,大大减小了显式绑定this对象的写法(callapplybind)。

可是,箭头函数并不适用于全部场合,因此如今有一个提案,提出了“函数绑定”(function bind)运算符,用来取代callapplybind调用。

【提案暂时不看】

 

  • 尾调用优化

尾调用(Tail Call)是函数式编程的一个重要概念,自己很是简单,一句话就能说清楚,就是指某个函数的最后一步是调用另外一个函数。

function f(x) {
  if (x > 0) {
    return m(x)
  }
  return n(x);
}

 

"尾调用"因为是函数的最后一步操做,因此不须要保留外层函数的调用帧,由于调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就能够了。

大大节省内存!

function f() {
  let m = 1;
  let n = 2;
  return g(m + n);
}
f();

// 等同于
function f() {
  return g(3);
}
f();

// 等同于
g(3);

反例子:

function addOne(a){
  var one = 1;
  function inner(b){
    return b + one;  // 由于这里,因此被迫在调用inner时还须要保留住var one,也就不是tail call了
  }
  return inner(a);
}

 

NB: ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。

这是由于在正常模式下,函数内部有两个变量,能够跟踪函数的调用栈。

    • func.arguments:返回调用时函数的参数。
    • func.caller:返回调用当前函数的那个函数。

 

  • 尾递归

对于尾递归来讲,因为只存在一个调用帧,因此永远不会发生“栈溢出”错误。  

function factorial(n) {
  if (n === 1) return 1;
  return n * factorial(n - 1);
}

factorial(5) // 120

--------------------------------------------------
改写成尾递归,只保留一个调用记录,复杂度由O(n) --> O(1) 。
function factorial(n, total) { if (n === 1) return total; return factorial(n - 1, n * total); } factorial(5, 1) // 120

 

可见,诀窍就在于:把全部用到的内部变量改写成函数的参数。

还有就是:只须要知道循环能够用递归代替,而一旦使用递归,就最好使用尾递归。

其余部分,柯里化(currying)【将多参数的函数转换成单参数的形式】详见连接。

 

尾递归优化只在严格模式下生效。

在正常模式下,或者那些不支持该功能的环境中,就是本身实现尾递归优化。 

减小调用栈?就是采用“循环”换掉“递归”。

详见原连接。

 

  • 函数参数的尾逗号

ES2017 容许函数的最后一个参数有尾逗号(trailing comma)。

这样的规定也使得,函数参数数组对象的尾逗号规则,保持一致了。

 

 

函数是一种对象

  • 处处都是”Object" 

基本值类型不是对象(number、string、Boolean、Undefined);

剩下的引用类型(函数、数组、null...)都是对象。

 

对象是经过函数建立的,而函数又是一种对象。那么这是为何呢?这就牵扯到prototype原型。 

 

相关文章
相关标签/搜索