markdown JavaScript的随笔

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了markdown JavaScript的随笔相关的知识,希望对你有一定的参考价值。

# JavaScript实现拖拽

在写代码前,先分析拖拽组件需要什么相关事件以及涉及到的位置属性<br>

## 拖拽的动作
拖拽某个目标,首先是在指定目标上按下鼠标(mousedown),确定了按下鼠标的状态和拖动前目标的位置后,
接着就是移动鼠标(mousemove),在移动的过程中,需要时刻改变目标的位置。
松开鼠标(mouseup)后,拖拽完成,并且重置拖拽状态。
<br>

注意,`mousemove`不能绑定在element上,一旦这样做,
鼠标点击在方块内之后,然后快速地拖拽方块(方块的面积是有限的),鼠标的焦点会跑离方块(肉眼上看上去方块跟不上鼠标的快速移动)。
松开鼠标后移动焦点回方块,不需要点击,方块也会跟焦点着移动。<br>
所以这里`mousemove`需要绑定在`document`上(鼠标拖动再快也不至于脱离整个页面吧)

<br>

## 位置属性
我们知道,JavaScript有很多的位置属性,在这里使用到了`offsetX`和`offsetY`,
点击时`position.offsetX`记录的就是鼠标焦点相对于目标的位置。
然后在`mousemove`事件中设置`element.syle.left=event.clientX-position.offset`,获取目标的移动距离
<br>

```html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<title>简易的拖拽</title>
	<style>
		#block {
            width: 200px;
            height: 200px;
            background-color: #000;
            position: absolute;
        }
	</style>
</head>
<body>
	<div id="block"></div>

	<script>
		function Drag(id){
			var element = document.getElementById(id);
			var position = {
				offsetX : 0,
				offsetY : 0,
				status : 0
			};

			element.addEventListener('mousedown',function(event){
				var e = event || window.event;
				position.offsetX = e.offsetX;
				position.offsetY = e.offsetY;
				position.status = 1;
			},false);

			document.addEventListener('mousemove',function(event){
				var e = event || window.event;
				if(position.status){
					position.endX = e.clientX;
					position.endY = e.clientY;
					element.style.position = 'absolute';
					element.style.top = position.endY - position.offsetY + 'px';
					element.style.left = position.endX - position.offsetX + 'px';
				}
			},false);

			element.addEventListener('mouseup',function(event){
				position.status = 0;
			},false);
		}

		Drag('block');
	</script>
</body>
</html>
```

# JavaScript中的this

this永远指向它执行的上下文环境,无论是apply、call、bind,都是为了给this指定上下文环境<br>

## 不同作用域下的this

### 全局作用域下的this
this在函数之外,无论是否为严格模式,都是指向window<br>
所以`this===window`<br>

### 函数作用域下的this
函数作用域内的this情况较为复杂,但是总体而言,this取决于函数如何调用。只有在函数被调用时,才存在this的指向<br>

#### 函数直接调用
普通模式下,直接调用函数,里面的this指向window
```javascript
 var t = 1;
 function test(){
    var t = 2;
    console.log(this.t);
}
test();  //1


(function test(){
  //"use strict"; 严格模式下this指向undefined
  console.log(this) // window
})();
```

严格模式下,直接调用函数,内部this指向该函数运行的环境<br>

```javascript
function test(){
    "use strict";
    return this;
}
console.log(test());  //undefined
```
上述demo中,尽管函数是在window下直接运行,但是由于没有被调用而是直接运行,左移this是undefined<br>
假如改成下面:<br>
```javascript
function test(){
  "user strict" //严格模式
  return this;
}
console.log(window.test()) //window
```
使用了window来调用函数,那么函数执行环境就是window<br>

所以在函数作用域中,通过什么对象来调用方法,运行环境就是该对象所在的上下文,方法内的this指向该对象<br>

就比如 `window.test()` ,运行环境就是window所在是上下文<br>

#### 对象中的this
当对象调用自己内部方法,this指向该对象<br>
```javascript
var obj = {
  id: 9,
  test: function() {
    return this.id;
  }
};
console.log(obj.test()); //9
```
这个demo中,test方法是对象obj内部的方法,通过obj调用test方法,那么该函数的运行环境就是obj所在的上下文,因而this指向obj<br>

如果是按照匿名函数的方式调用,依旧是指向对象:<br>

```javascript
var obj = {id: 9};
function test() {
  return this.id;
}
obj.t = test;
console.log(obj.t()); // logs 9
```

#### 原型链中的this

例子:<br>
```javascript
var o = {
  f : function(){ 
    return this.a + this.b; 
  }
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5
```
由前面提到的执行环境,在对象o没有被调用的时候,函数f内的this是没有指向的<br>
当创建了一个继承自o的新对象p,那么p同时继承了f,通过调用p.f(),则this的执行环境指向对象p<br>


#### getter和setter中的this

get用来获取对象属性,set就是设置对象属性<br>

```javascript
var obj = {
  a: 1,
  b: 2,
  get test(){
    return this.a + this.b;
  },
  set f(x) {
    this.a = x;
    this.b = x + 1;
  }
};
console.log(obj.test) //3
obj.f = 5
console.log(obj.test) //11
```

当对象obj调用自身方法时,也就是obj.test(),obj是test方法的运行环境,this指向obj。<br>
通过set设置对象obj属性后,a和b分别变为5和6,之后再调用obj.test。但是obj依旧是test方法的运行环境,
所以无论怎么调用对象内部函数中的this,依旧坚持指向运行环境,也就是对象本身,当然其他非继承的对象也无法调用其内部的方法,所以this可以看作只认一个爸爸<br>

#### 构造函数内的this
```javascript
function Templete(){
  this.a = 8;
}
var t = new Templete();
console.log(t.a); // 8
```

<b>new实例化的对象就是一个运行环境,所以构造函数中的this就是指向实例化的对象</b><br>


#### call和apply中的this

call和apply方法第一个参数就是绑定函数的运行环境<br>

```javascript
function add(x) {
  return this.a + x
}
var obj = {
  a: 10
}
var sum = add.call(obj, 8)
//var sum = add.apply(obj, [8])
console.log(sum) // 18
```

这里,我们定义了一个函数add,用来求a+x,但是a是this.a指代的,如果直接运行add(x),无法给出a的值,此时就需要apply和call了<br>
知道了this.a从何而来,我们就可以通过apply和call将add里面的this绑定到obj对象中<br>
所以这里add方法内的thi指向obj<br>

#### bind中的this
它的第一个参数也是用来绑定this指向的对象<br>
```javascript
function f(x){
  return this.a + x;
}
var obj = {
  a: 10
}
var sum = f.bind(obj, 8);
console.log(sum()) // 18
```

#### DOM事件函数中的this
假如我们监听一个DOM元素,给该DOM元素绑定一个事件,那么事件函数内的this指向当前的DOM元素<br>
```javascript
var ele = document.getElementById('tab');
ele.addEventListener('click', func, false);
function func(e) {
    console.log(this === e.target) //true
}
```

#### 内联事件中this
这种情况和DOM事件函数的情况一样,也是指向对应的DOM元素<br>
`<div onclick="alert(this.tagName.toLowerCase())"></div>`<br>
打印出来的就是当前div标签<br>


## 概括总结
<ol>
    <li>全局范围:它会指向全局对象( 浏览器下指window)</li>
    <li>全局函数调用:它 还是指向全局对象。</li>
    <li>对象函数调用:调用某个对象的函数,它指向当前对象</li>
    <li>使用 new 实例化对象时:它指向 新创建的对象。</li>
    <li>调用某些方法时:如: Function.prototype 上的 call 或者 apply 方法 以及 with等它指向传入的对象</li>
</ol>

参考:<br>
[知乎 - 如何理解 JavaScript 中的 this 关键字?](https://www.zhihu.com/question/19636194)<br>
[如何理解 JavaScript 中的 this 关键字? - 二月的回答 - 知乎](https://www.zhihu.com/question/19636194/answer/153576642)<br>
[如何理解 JavaScript 中的 this 关键字? - 萧强的回答 - 知乎](https://www.zhihu.com/question/19636194/answer/25081397)<br>


<hr>

下面是补充:<br>
## 关于setTimeout和setInterval中this指向问题
在慕课网TypeScript入门课程中,练习一个小demo发现了问题,:<br>

```javascript
function getStock(name){
	this.name = name;
	setInterval(function(){
		console.log("name1 is:"+this.name);
	},2000);
}
var stock = new getStock("test");
```
上述最后的输出结果不是期待中的test,而是undefined,说明此时的this指向了window对象<br>
原因就在于类似setInterval这样的函数在调用代码时运行在域所在函数完全分离的执行环境上。<br>
要解决这个问题,可以参考下面的3个方法:<br>

###1.将当前对象的this保存为一个变量,然后定时器内的函数用闭包访问该变量

```javascript
function getStock(name){
  var that = this;
	this.name = name;
	this.getfunc = function(){ 
  	setInterval(function(){
  		console.log("name1 is:"+that.name);
  	},2000);
  }
}
var stock = new getStock("test");
stock.getfunc();   
//test

function getStock(name){
  var that = this;
	this.name = name;
	(function(){ 
  	setInterval(function(){
  		console.log("name1 is:"+that.name);
  	},2000);
  })()
}
var stock = new getStock("test");
//test
```

###2.用bind改绑定this

```javascript
function getStock(name){
	this.name = name;
  	setInterval(function(){
  		console.log("name1 is:"+this.name);
  	}.bind(this),2000);
}
var stock = new getStock("test");
```
当被绑定函数执行时,bind会创建一个新函数,并将第一个参数作为新函数运行的this。<br>
同时,也可以使用apply和call方法,不过要注意call会在调用后立即执行,相当于失去延时效果<br>

###3.箭头函数

```javascript
function getStock(name){
	this.name = name;
  	setInterval(() => {
  		console.log("name1 is:"+this.name);
  	},2000);
}
var stock = new getStock("test");
```
ES6标准中的箭头函数的this总是指向词法作用域,也就是外层调用者<br>

参考:<br>
[关于setInterval和setTImeout中的this指向问题](https://www.cnblogs.com/zsqos/p/6188835.html)<br>


## 慕课网课程关于上下文环境的教程

[慕课网课程关于上下文环境的教程(https://www.imooc.com/video/7558)<br>

this的使用:
<br>

*1.作为对象的方法
this在方法内部,this就指向调用这个方法的对象
<br>

```javascript
var pet = {
  words = 'miao',
  speak : function(){
    console.log(this.words);
    console.log(this === pet);
  }
}
pet.speak();
//miao
//true
```

*2.函数的调用
this指向执行环境中的全局变量(浏览器->window || nodejs ->global)
<br>

```javascript
function pet(words){
  this.words = words;
  console.log(this.words);
  console.log(this);
}
pet('miao');

//miao
//window
```

3.构造函数
this所在的方法被实例对象所调用,那么this就指向这个实例对象
<br>

```javascript
function pet(words){
  this.words = words;
  this.speak = function(){
    consoel.log(this.words);
  }
}

var cat = new pet('miao');
cat.speak();

//miao
```

### 继承

```javascript
function pet(words){
  this.words = words;
  this.speak = function(){
    console.log(this.words);
  }
}

function dog(words){
  pet.call(this,words);
  pet.apply(this,arguments);
}

var dog = new dog('wang');
dog.speak();
//wang
```

# 本文是有关JavaScript的一些零散的笔记

### 判断一个变量是否为数组
常用的方法:<br>
```javascript
var arr = [];

//isArray方法
Array.isArray(arr);

//instanceof
arr instanceof Array;

//constructor
arr.constructor === Array

//Array.prototype.isPrototypeOf()
Array.prototype.isPrototypeOf(arr);

//Object.getPrototypeOf()
Object.getPrototypeOf(arr) === Array.prototype

//Object.prototype.toString.apply()
Object.prototype.toString.apply(a) === '[object Array]'
```
一般来说,多数主流框架都使用最后一种方法判断。另外要补充,这种方法不仅能判断是否为数组,还能判断是否为字符串等等。<br>
demo:<br>
```javascript
Object.prototype.toString.call(2);
Object.prototype.toString.call(new Object);
```


### split、slice、splice的分辨
* slice:    不会修改原来的数组,截取数组元素返回
<br>
语法:`slice(start,[end])`
<br>

```javascript
    var arr1 = ["a", "b", "c", "d", "e", "f"];
    // 从下标为0的位置开始截取,截取到下标2,但是不包括下标为2的元素. 原数组没有任何的变化
    var newArr = arr1.slice(0, 2);
    alert(newArr);// a, b
    alert(arr1.slice(1, 4)); // b,c,d
    //从下标为2的元素开始截取,一直到最后一个元素
    alert(arr1.slice(2));  //c,d,e,f
    //从倒数第5个元素,截取到倒数第2个
    alert(arr1.slice(-5, -2)); // b c d
```


* splice:    直接修改原数组,删除或替换原数组中的指定元素,返回的是被删除或替换的元素组成的数组(注意区分返回值和被操作值)
<br>

```javascript
var color = new Array('red','blue','yellow','black');

console.log(color.splice(1,2));  //删除color中的1、2两项
//"blue", "yellow"

color.splice(1,0,'brown','pink');  //在color键值为1的元素前插入两个值
console.log(color);
//"red", "brown", "pink", "blue", "yellow", "black"

console.log(color.splice(1,2,'brown','pink'))
//"blue", "yellow"
console.log(color)
//"red", "brown", "pink", "black"

```

* split:    根据特定的字符切割字符串并且返回生成的数组
<br>

 ```javascript
str = "Hello!World";
str2 = str.split("!");
console.log(str2);  //Hello,World
console.log(str2.length);  //2
```

### 数组去重

indexof:<br>

```javascript
function unique(arr){
  var a = []
  for(var i=0;i<arr.length;i++){
    if(a.indexOf(a[i]) == -1){
      a.push(arr[i]);
    }
  }
  return a;
}
```
但是这个方法不能识别有`NaN`的方法,比如有`var a = [1,1,2,3,4,NaN,NaN];`,最后得出的结果是
`[1, 2, 3, 4, NaN, NaN]`
<br>



filter+indexof:<br>

```javascript
function uniuqe(arr){
  var res = arr.filter(function(item,index,array){
    return array.indexOf(item) === index;
  })
  return res;
}

//升级版:排序去重
function unique(arr){
  return arr.concat().sort().filter(function(item,index,array){
    return !index || item !== array[index-1]
  })
}
```
这个方法本质上也是用`indexOf`,所以也不能识别`NaN`
<br>

#### ES6的新方法
ES6提供了`Set`数据结构,它类似于数组(具有 iterable 接口),但是其成员是唯一的,利用这个特效可以实现数组去重<br>

```javascript
function unique(arr){
  return arr.from(new Set(arr));
}

//更加简化
function unique(arr){
  return [...new Set(arr)];
}
//最残暴的简化
var unique = (a) => [...new Set(a)]
```
在前面提到了ES5的几种方法中均认为内部的多个`NaN`都是不同的,但是在ES6中,在`Set`内部,两个`NaN`是相等
所以,就可以去除重复的`NaN`
<br>


ES6还有一种数据结构`Map`,同样可以用来实现:<br>

```javascript
function unique(arr){
  const m = new Map();
  return arr.filter((a) => !m.has(a) && m.set(a,1))
}
```
对于`Map`结构,如果对同一个键多次赋值,后面的值将覆盖前面的值。<br>
所以,也可以实现`NaN`去重<br>

<br>

参考:<br>

[Github-JavaScript专题之数组去重](https://github.com/mqyqingfeng/Blog/issues/27)<br>
[数组去重](https://wy1009.github.io/2017/03/09/%E6%95%B0%E7%BB%84%E5%8E%BB%E9%87%8D/#more)<br>
# JavaScript随笔

<b>说明:</b>
本篇是JavaScript的一些零散的知识点汇集<br>

## Array.prototype.slice.call(arguments)将对象转换为数组

`Array.prototype.slice.call(arguments)`能将具有length属性的对象转成数组,出来IE的节点集合<br>

首先,`slice([begin],[end])`方法返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象。
原始数组内容不变<br>

```JavaScript
var arr = ['a','b','c','d'];
console.log(arr.slice(1,3));
//['b','c']
```
`slice` 不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。原数组的元素会按照下述规则拷贝:
<ul>
    <li>如果该元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。
    如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。</li>
    <li>对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。
    在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。</li>
</ul>

### 将类似数组的对象/集合转换为数组

```javascript
function list() {
  return Array.prototype.slice.call(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]
```

除了使用 `Array.prototype.slice.call(arguments)`,你也可以简单的使用 `[].slice.call(arguments)` 来代替。
另外,你可以使用 `bind` 来简化该过程。<br>

```javascript
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);

function list() {
  return slice(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]
```

### 通用函数

```javascript
var toArray = function(s){
    try{
        return Array.prototype.slice.call(s);
    } catch(e){
            var arr = [];
            for(var i = 0,len = s.length; i < len; i++){
                //arr.push(s[i]);
                   arr[i] = s[i];  //据说这样比push快
            }
             return arr;
    }
}
```

参考:<br>
[MDN - Array.prototype.slice()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)<br>
[CSDN - Array.prototype.slice.call()方法详解](http://blog.csdn.net/i10630226/article/details/49702375)<br>
# JavaScript中for...of...和for...in...的区别

二者都是迭代的方法,区别在于迭代方式<br>
迭代的概念可以看作遍历<br>

最早数组遍历的方式:<br>
```javascript
var arr = [1,2,3];
for(var i=0;i<arr.length;i++)
    console.log(arr[i]);
```

ES5后引入了`forEach`:
```JavaScript
var arr = [1,2,3];
arr.forEach(function(a){
    console.log(a);
});
```
虽然简洁了一些,不过还是存在一些缺陷的:<br>
1.不能使用break退出循环<br>
2.不能使用return返回内容到外层<br>

所以我们想到了`for...of`和`for...in`两个语句
<br>

说明:`let...of`和`for...of`等效;`let...in`和`for...in`等效<br>

`for...in`语句以任意顺序遍历一个对象的可枚举属性(表面上循环结果是key)。对于每个不同的属性,语句都会被执行。<br>

```JavaScript
var a = ["a", "b", "c"];
a.name = 'test';
for(var index in a){
  console.log(a[index]);
  //console.log(typeof index);
}
//a  b  c  test
```
上述代码正确地将数组内的元素遍历了出来,<b>但是同时将数组的属性也一并打印出来了</b>

因此可以看出,作用于数组的for-in循环除了遍历数组元素以外,还会遍历自定义属性<br>

同时要注意:<br>
<b>for...in是按照随机顺序遍历数组的;假如数组内元素是String,可能会无意间进行字符串运算</b>
<br>

为了解决前面方法的不足,ES6引入了`for...of`<br>

`for...of`语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。<br>

```JavaScript
var a = ["a", "b", "c"];
a.name = 'test';
for(var value of a){
  console.log(value);
  //console.log(typeof value);
}
//a  b  c 
```
和`for...in`相反,它不会循环出对象的key,只会循环出数组的value,因此它不能循环遍历普通对象。
不过假如和`Object.keys()`一起使用,可以线获取对象的所有key的数组,然后再遍历出value<br>
```
var student={
    name:'wujunchuan',
    age:22,
    locate:{
    country:'china',
    city:'xiamen',
    school:'XMUT'
    }
}
for(var key of Object.keys(student)){
    //使用Object.keys()方法获取对象key的数组
    console.log(key+": "+student[key]);
}
//不好的方式

for(var prop in  student){
  console.log(prop+': '+student[prop]);
}
//好的方式
```


和`for...in`的另外一个对比示例:
```JavaScript
Object.prototype.objCustom = function() {}; 
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';
/*
每个对象将继承objCustom属性,并且作为Array的每个对象将继承arrCustom属性,因为将这些属性添加到Object.prototype和Array.prototype。
由于继承和原型链,对象iterable继承属性objCustom和arrCustom。
*/

for (let i in iterable) {
  console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
}
/*
此循环仅以原始插入顺序记录iterable 对象的可枚举属性。它不记录数组元素3, 5, 7 或hello,因为这些不是枚举属性。
但是它记录了数组索引以及arrCustom和objCustom。
*/

for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i); // logs 0, 1, 2, "foo"
  }
}
/*
这个循环类似于第一个,但是它使用hasOwnProperty() 来检查,如果找到的枚举属性是对象自己的(不是继承的)。
如果是,该属性被记录。记录的属性是0, 1, 2和foo,因为它们是自身的属性(不是继承的)。
属性arrCustom和objCustom不会被记录,因为它们是继承的。
*/

for (let i of iterable) {
  console.log(i); // logs 3, 5, 7
}
/*
该循环迭代并记录iterable作为可迭代对象定义的迭代值,这些是数组元素 3, 5, 7,而不是任何对象的属性。
*/
```

### 总结一下

<ul>
    <li>for...in 语句以原始插入顺序迭代对象的可枚举属性。
        for...of 语句遍历可迭代对象定义要迭代的数据。</li>
    <li>for...in循环出的是key,for...of循环出的是value</li>
    <li>for...of不能循环普通的对象,需要通过和Object.keys()搭配使用。此时用for...in就很好</li>
</ul>


参考:<br>
[MDN - for...of语句](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/for...of)<br>
[MDN - for...in语句](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/for...in)<br>
[SegmentFault - javascript总for of和for in的区别?](https://segmentfault.com/q/1010000006658882)<br>
[Javascript中的for-of循环](https://github.com/wujunchuan/wujunchuan.github.io/issues/11)<br>
# JavaScript字符串截取函数slice、substring、substr

## substring
功能:返回一个索引和另外一个索引之间的字符串<br>
语法:`str.substring(start,[end])`<br>
<br>
注意:
<ul>
    <li>截取范围包括start但是不包括end</li>
    <li>若start==end,则返回空</li>
    <li>end可省略,如果省略则表示从start截取到字符串末尾</li>
    <li>若某个参数是NaN或者小于0,那么等效为0;若某个参数大于字符串长,那么等效为字符串长</li>
    <li>若start大于end,那么start和end交换,比如str.substring(2,0) == str.substring(0,2)</li>
</ul>
<br>

demo:<br>
```javascript
var str = 'abcdefghij';
console.log('(1, 2): '   + str.substring(1, 2));   // '(1, 2): b'
console.log('(1, 1): '   + str.substring(1, 1));   // '(1, 1): '
console.log('(-3, 2): '  + str.substring(-3, 2));  // '(-3, 2): ab'
console.log('(-3): '     + str.substring(-3));     // '(-3): abcdefghij'
console.log('(1): '      + str.substring(1));      // '(1): bcdefghij'
console.log('(-20, 2): ' + str.substring(-20, 2)); // '(-20, 2): ab'
console.log('(2, 20): '  + str.substring(2, 20));  // '(2, 20): cdefghij'
console.log('(20, 2): '  + str.substring(20, 2));  // '(20, 2): cdefghij'

```

<br>


## substr
功能:返回从指定位置开始的字符串中指定字符数的字符串<br>
语法:`str.substr(start,[length])`<br>

<br>
注意:
<ul>
    <li>它会从start位置截取长度为length的字符串</li>
    <li>若start是正数且大于或等于字符串长度,则返回一个空字符串</li>
    <li>若start为负数,则将该值加上字符串长度再进行计算,若还是负数,则从0开始截取(实际上就是倒过来从尾部开始截取)</li>
    <li>若length为0或负数,则返回空;若length省略,则截取到末尾</li>
</ul>

<br>

demo:<br>

```javascript
var str = 'abcdefghij';
console.log('(1, 2): '   + str.substr(1, 2));   // '(1, 2): bc'
console.log('(-3, 2): '  + str.substr(-3, 2));  // '(-3, 2): hi'
console.log('(-3): '     + str.substr(-3));     // '(-3): hij'
console.log('(1): '      + str.substr(1));      // '(1): bcdefghij'
console.log('(-20, 2): ' + str.substr(-20, 2)); // '(-20, 2): ab'
console.log('(20, 2): '  + str.substr(20, 2));  // '(20, 2): '
```

## slice
功能:返回一个索引到另一个索引间的字符串<br>
语法:`str.slice(start,[end])`<br>
<br>
注意:
<ul>
    <li>start和end都为正数时,截取范围不包括start和end</li>
    <li>若start为负数,start=start+length,若还是负数,则从0开始截取(实际上就是倒过来从尾部开始截取)</li>
    <li>若start大于或等于字符串长,则返回空</li>
    <li>若end省略,则截取到字符串末尾;若end为负数,则end=length+end</li>
</ul>
<br>
demo:<br>

```javascript
var str = 'abcdefghij';
console.log('(1, 2): '   + str.slice(1, 2));   // '(1, 2): b'
console.log('(-3, 2): '  + str.slice(-3, 2));  // '(-3, 2): '
console.log('(-3, 9): '  + str.slice(-3, 9));  // '(-3, 9): hi'
console.log('(-3): '     + str.slice(-3));     // '(-3): hij'
console.log('(-3,-1): ' + str.slice(-3,-1));     // '(-3,-1): hi'
console.log('(0,-1): '  + str.slice(0,-1));     // '(0,-1): abcdefghi'
console.log('(1): '      + str.slice(1));      // '(1): bcdefghij'
console.log('(-20, 2): ' + str.slice(-20, 2)); // '(-20, 2): ab'
console.log('(20): '     + str.slice(20));  // '(20): '
console.log('(20, 2): '  + str.slice(20, 2));  // '(20, 2): '

```

补充:<br>
### 注意和split的区分
split是将一个字符串分割为字符串数组<br>

demo:<br>

```javascript
"hello".split("")	//可返回 ["h", "e", "l", "l", "o"]
"2:3:4:5".split(":")	//将返回["2", "3", "4", "5"]
```

<br>
参考:<br>
[掘金-JS字符串截取函数slice(),substring(),substr()的区别](https://juejin.im/post/59e2af3151882578cf573319)<br>

以上是关于markdown JavaScript的随笔的主要内容,如果未能解决你的问题,请参考以下文章

markdown git的使用随笔

markdown 随笔

markdown 前端随笔

新功能发布!Markdown写博客!

第二个随笔

Mrakdown随笔