js基础

js 笔记

javaScript的历史

java和javaScript的关系

JavaScript和Java是两种不一样的语言,但是它们之间存在联系。

JavaScript的基本语法和对象体系,是模仿Java而设计的。但是,JavaScript没有采用Java的静态类型,java需要编译器编译运行。

javaScript与ECMAScript的关系

ECMAScript 是一套语言标准,包含:

  1. javascript
  2. jscript
  3. actionscript
javaScript与ECMAScript的区别

ECMA是javascript的标准,javascript是ECMA的实现。

js代码引入

  1. 使用内嵌方式引入js代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>

  <!-- 内联引用 -->
  <script>
  var name='张三';
  </script>
</head>
  1. 外联方式引入代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>

  <!-- 外联引用 -->
  <script src="js/main.js"></script>
</head>
<body>

当script标签和link标签同时出现在head区域,那建议script标签写在link标签的后面,这样以保证样式优先加载,不会因为js代码执行阻塞页面渲染。

如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <link rel="stylesheet" href="css/style.css">
  <script src="js/main.js"></script>
</head>
<body>

根据YUI前端性能小组建议script标签放置在</body>的前面最佳。

如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <link rel="stylesheet" href="css/style.css">
</head>
<body>

  <!-- 外联的方式引用 -->
  <script src="js/main.js"></script>

</body>
</html>

js注释

  1. 单行注释 //
<script>
//注释1
//注释2
//注释3
var name='张三';
</script>
  1. 多行注释
<script>
/*
注释1
注释2
注释3
*/
var name='张三';
</script>

优先考虑单行注释,因为多行注释有可能会造成语法错误。

语句和表达式

语句和表达式的区别

语句指一个为了得到返回值的计算式。语句和表达式的区别在于,前者主要为了进行某种操作,一般情况下不需要返回值;后者则是为了得到返回值,一定会返回一个值。

  1. js代码通常是以英文状态分号结束;
  2. js代码会自动过滤字符之间空格、换行、缩进
  3. js代码严格区分大小写

变量

变量是对值的引用,变量就是为“值”起名,然后引用这个名字。

var sum = 1+3*10;
alert(sum);//警告框输出
document.write(sum);//文档输出
console.log(sum);//控制台输出,调试代码

变量命名规则

声明变量使用关键字var,使用此关键字声明的变量没有作用域

如果想要有作用域,可以使用es6种的letconst这两个关键字。

声明一个变量,一次都没有给它赋值,那么它的值是未定义undefined

  1. 单个逐行声明
var a = 1;
var b = '您好';
var c = 20;
  1. 多个一次声明
var a = 1,
    b = '您好',
    c = 20;

  1. var变量会自动提升
console.log(a);//undefined
var a = 10;

// 以上代码变量会自动提升,相当于下面的代码
var a;
console.log(a);
a = 10;

仔细理解一下代码的区别

//会提升
console.log(a);
var a = 10;

//会报错,因为没有var关键字
console.log(a);
a = 10;

  1. 变量命名要求

语法

var 变量名 =;

变量名,以字母、下划线、KaTeX parse error: Expected group after '_' at position 29: …母、数字、中文。特殊符号仅限`_̲`,变量字符之间不要存在空格。

变量命名不能使用(保留字)关键字 if、else、break、with等等

变量命名严格区分大小写

// 错误的命名
var 1a = 10;
var a 1= 20;
var %a = 30;
var a,3 = 20;
var var = 10;
var if = 20;

//正确的命名
var a1 = 20;
var A1 = 30;
var _abc = 10;
var $ab = 20;
var 姓名 = '张三';

  1. 变量命名风格

Camel Case 小驼峰式命名法

var fullName = '张亮';

Pascal Case 大驼峰式命名法

var FullName = '张亮';

常量命名,通常全部大写

//比如圆周率就是典型的常量
var PI = 3.14;

条件语句

if条件语句

  1. if 单条件语句
var age = 30;
if ( age >= 18) { 
  console.log( '您已经成年!' );
}

// 条件语句也可以使用下面的风格
var age = 30;
if ( age >= 18)
{ 
  console.log( '您已经成年!' );
}

//简写,仅限条件语句中只有一条语句时成立。
var age = 30;
if ( age >= 18) console.log( '您已经成年!' );


  1. if else 双条件语句
var age = 30;
if ( age >= 18 ) { 
  console.log( '您已经成年!' );
}else{
  console.log( '未成年!' );
}

//简写,仅限条件语句中只有一条语句时成立,不推荐这样写。
var age = 12;
if ( age >= 18)
  console.log( '您已经成年!' );
else
  console.log( '未成年!' );

  1. if else if else 多条件语句

&& 并且
||或者

// < 60 不及格
// >= 60 && <70 及格
// >= 70 && <80 良好
// >= 80 && <90 优秀
// >= 90 极好

var score = prompt('请输入你的分数:');

if ( score < 60 ){
  console.log('不及格');
}else if( score >= 60 && score < 70 ){
  console.log('及格');
}else if( score >= 70 && score < 80 ){
  console.log('良好');
}else if( score >= 80 && score < 90 ){
  console.log('优秀');
}else{
  console.log('极好');
}

switch条件语句

语法

switch( 表达式 ){
  case1:
    //跳出条件语句
    break;
  case2:
    //跳出条件语句
    break;
  case3:
    //跳出条件语句
    break;
  default:
    //输出默认结果
}

注意:switch执行值是严格匹配的,也就是说 5 不等于 '5'

示例

var day = new Date().getDay();

switch( day ){
  case 0:
    console.log('星期日');
    break;
  case 1:
    console.log('星期一');
    break;
  case 2:
    console.log('星期二');
    break;
  case 3:
    console.log('星期三');
    break;
  case 4:
    console.log('星期四');
    break;
  case 5:
    console.log('星期五');
    break;
  case 6:
    console.log('星期六');
    break;
  default:
    console.log('无效的值');
}

循环语句

  1. for循环语句

语法:

for ( 初始值;条件表达式;变量 ) {
  // 循环体内语句
}

示例

for ( var i = 0;i<100;i++ ) {
  console.log(i);
}

循环也可以把括号中的语句提出到外面,或者置入循环体中

//初始语句外置
var i = 0;
for ( ;i<100;i++ ) {
  console.log(i);
}

//变量语句内置
for ( var i = 0;i<100; ) {
  console.log(i);
  i++;
}

for循环支持嵌套循环

for{
  for(){
  }
}

示例

//1. 循环输出 0-99
for ( var i = 0;i<100; i++  ) {
  console.log(i);
}

//2. 循环输出 99-0
for( var i=99; i >= 0; i-- ){
  console.log( i ); 
}

//3. 循环输出 0-100的偶数  10 % 3 == 1 , 10 % 5 == 0 % 表示 取余数
for ( var i = 0;i<100; i++  ) {
  if(  i % 2 == 0 ){
    console.log(i);
  }
}


//4. 循环输出 0- 100 的总和
var sum = 0;
for ( var i = 0;i<=100; i++  ) {
  sum = sum + i;//累积
}
console.log( sum );

//5. 九九乘法表
for( var i=1; i<=9; i++ ){

  for( var j = 1; j <= i; j++ ){
    document.write( j, '*', i, '=', i*j,'&nbsp;&nbsp;&nbsp;' );
  }
  document.write('<br>');

}

breakcontinue关键字

  1. while循环语句

语法

while( 条件表达式 ){
  //循环体语句
  //变量
}

示例

var i=0;
while(i<100){
  console.log(i);
  i++;
}

var i=99;
while(i>=0){
  console.log(i);
  i--;
}

breakcontinue可以用循环语句中,用来跳出循环

var i=0;
while(i<100){
  i++;//放前面,不然会死循环
  if(i==50) continue;
  console.log(i);
}

forwhile语句可以混合使用

  1. do while循环语句

do while 无论条件真或假,至少执行一遍。

语法

do {
  // 循环体语句
} while ( 条件表达式 );

示例

var i=0;
do {
  console.log(i);//0
} while ( i > 10 );

js数据类型

数值 Number

js只有一种数值类型,浮点类型。

var age = 20; //正整型
var price = 34.2; //浮点型
var price = -30; //有符号整型

科学计数法

123e3 // 123000
123e-3 // 0.123
-3.1E+12
.1e-23

正零和负零

-0 === +0 // true
0 === -0 // true
0 === +0 // true

NaN 表示非数字,它的数据类型是数值类型

注意:NaN不等自己

NaN == NaN //false 

字符串 String

通常用双引号或单引号包裹的字符,我们称为字符串

单引号里面可以使用双引号,双引号里面可以使用单引号

var name = "张三";
var name = '张三';

//双引号中嵌套使用单引号
var str = "I'm a boy";

//单引号中嵌套使用双引号
var str = '我喜欢吃"苹果"';

//使用转义符,可以安全的输出。
var str = 'I\'m a boy';
var str = "我喜欢吃\"苹果\"";


推荐:优先考虑使用单引号

字符串和变量拼接

字符串和变量用加号+连接:

  • 变量在开头,只需要右加号
var name = '张三';
var str = name + '是我!';

  • 变量在中间,需要左右两个加号
var name = '张三';
var str =  '我是' + name + '本人。';

  • 变量在末尾,只需要左加号
var name = '张三';
var str =  '我是' + name;

示例

var name = '张三';
var age = 19;
var xueli = '本科';
console.log('我叫' + name +',今年'+age+'岁。\n 我的学历是'+xueli);

字符串代码不能强制折行,会报错,解决方案如下:

  • 字符串拼接换行(差,书写麻烦)
  • 末尾加转义符换行(良好)
  • es6字符串模板(好用,但要考虑兼容性)
<script>
var name = '张三';
var age = 20;
var xueli = '本科';
var str = `<table border="1">
  <tr>
    <th>${name}</th>
    <th>${age}</th>
    <th>${xueli}</th>
  </tr>
</table>`;
document.write( str );
</script>

布尔类型 Boolean

布尔类型只有两个值,true 真和 false

布尔相关运算符

  1. &&:并且,和,与的意思
  2. ||: 或者的意思
  3. !:取反,否定,排除的意思
  4. ===:严格等于
  5. ==:等于
  6. !==:严格不等于
  7. !=:不等于
  8. >:大于
  9. >=:大于或等于
  10. <: 小于
  11. <=:小于或等于

===== 的区别

==它会转换两端的数据类型,以保持相等。
===它不会转换两端的数据类型,严格等于。

console.log( 1 == '1' );//true 隐式转换
console.log( 1 === '1' );//false

js有那些值会自动转换为假?

  • false 假值
  • 0 数值零
  • '' 空字符串
  • null
  • undefined 未定义
  • NaN 非数字

注意:[]和{} 空数组和空对象都是真值

字符串中注意,只要有值,都是为真值,比如:'0'' ''false'都是真值

未定义 undefined

表示一个变量声明了,但是从来没有赋值

空 null

表示变量为空,null 它本质上是一个对象。

undefinednull的区别:

  • undefined 表示声明变量,未赋值
  • null 表示变量值为空
  • undefined 是基本数据类型
  • null 通过typeof检测它是对象类型

函数 function

函数本身也是一个值,函数允许将多行代码封装起来,然后通过函数名调用,方便代码组织和管理。

function add( c, d){
  console.log(c+d );
}
add( 1, 3 );

数组 array

数组是一种集合,存入各种数据类型,可以很方便存取操作,数组是有序的。

var arr = ['苹果','桔子'];
console.log( arr[1] );

对象 object

对象是变量的集合,对象是无序的。

var person = {
  age: 20,
  name: '张三',
  chengnian: true,
  habby: ['篮球','美食']

}
console.log( person.habby[1] , person['habby'][1] );

符号 symbol

es6 新增的数据类型,表示唯一值

数据类型总结

js基本类型有那些?

  • 数值
  • 字符串
  • 布尔
  • 对象
  • undefined

js 有那些数据类型?

  1. 数值
  2. 字符串
  3. 布尔
  4. 对象
  5. 未定义 undefined
  6. 符号 symbol
  7. 空 null

typeof 检测数据类型

请问typeof能检测的数据类型有那些?

  1. number
  2. string
  3. boolean
  4. object
  5. undefined
  6. function
  7. symbol
var a = 10;
var b = '字符串';
var c = true;
var d = [];
var e = {};
var f = Symbol();
var h = null;
var i = undefined;
var g = function(){};
var j = NaN;
var k = '';

console.log( typeof a); // number
console.log( typeof b); // string
console.log( typeof c); // boolean
console.log( typeof d); // object
console.log( typeof e); // object
console.log( typeof f); // symbol
console.log( typeof h); // object
console.log( typeof i); // undefined
console.log( typeof g); // function
console.log( typeof j); // number
console.log( typeof k); // string

因为typeof检查对象和数组返回都是object,如何区分?

var o = {};//对象
var a = [];//数组

//方法1
o instanceof Array // false
a instanceof Array // true

//方法2
Array.isArray( o ) // false
Array.isArray( a ) // true

//方法3


数值类型

与数值相关的全局方法:

  1. parseInt() 将字符串转换数值整型
var a = '100px';
var b = '100 200';
var c = '¥100';
var d = '';
var e = '2e3';

console.log( parseInt(a) );// 100
console.log( parseInt(b) );// 100
console.log( parseInt(c) );// NaN
console.log( parseInt(d) );// NaN
console.log( parseInt(e) );// 2

  1. parseFloat() 将字符串转换数值浮点型
  2. number() 将字符串转换为数值
var a = '100px';
var b = '100 200';
var c = '¥100';
var d = '';
var e = '2e3';
var f = '200.67';

console.log( Number(a) ); // NaN
console.log( Number(b) ); // NaN
console.log( Number(c) ); // NaN
console.log( Number(d) ); // 0
console.log( Number(e) ); // 2000
console.log( Number(f) ); // 200.67

  1. isNaN:判断是否是非数字,返回真与假

isNaN 它会隐性的将字符串转为数字,然后判断它是否是非数字

var a = '10';
var b = '十';
console.log( isNaN(a) );//false
console.log( isNaN(b) );//true

数值实例的方法

toFixed 将数值保留指定的小数位数,支持四舍五入。

var price = 100.679;
console.log( (200).toFixed(2) );  //正整型要加括号,不然会报错
console.log( 120.679.toFixed(2) ); //浮点不需要
console.log( '120.679'.toFixed(2) ); //报错,因为toFixed是数值的方法,不是字符串的方法
console.log( price.toFixed(2) );//100.68

给数值实例扩展方法

//扩展方法1
Number.prototype.CNY = function( len ){
  return '¥'+this.toFixed( len );
}
var num = 100;
console.log( num.CNY( 2 ) );


//扩展方法2
Number.prototype.add = function( n ){
  return this + n;
}
var a = 1;
console.log( a.add(2).add(5) );

隐式数据类型转换有那些?

console.log( 1 == '1' );  //true
console.log( 1 - '1' );  //0
console.log( 2 - true ); //1
console.log( 2 - [] ); //2
console.log( 2 - '' ); //2

字符串类型

字符串就是零个或多个排在一起的字符,放在单引号或双引号之中。

var a = 张三; //错误,必须加引号
var a = '张三';

转义符

常用转义符

  • \n:换行符
  • \r:回车键
  • \':单引号
  • \":双引号
  • \\:反斜杠
  • \t:制表符
字符串与数组
  1. 数组和字符串 都有长度,也有下标(索引值)
  2. 数组的长度可读写,而字符串只可读,不可写。
  3. 字符串不可以通过索引值方式改变成员的值。
  4. 字符串是类数组对象
//数组
var arr = ['你','今','天','心','情'];

//类数组对象
var obj = {
  0: '你',
  1: '今',
  2: '天',
  3: '心',
  4: '情',
  length: 5
}

//类数组对象
var str = '你今天心情';

console.log( arr[1], obj[1] );

arr.length = 10;
obj.length = 10;

console.log( arr.length, obj.length );


var arr = ['你','今','天','心','情'];
var str = '你今天心情';
arr[1] = '昨';//可以写入
str[1] = '昨';//字符串不可以写入

console.log( arr , str );


Base64 转码
var str = 'Hello World!';

//将ASCII编码转换为base64编码
console.log( btoa( str ) );//SGVsbG8gV29ybGQh

var base = 'SGVsbG8gV29ybGQh';

//将base64编码转换为ASCII编码
console.log( atob( base ) );//Hello World!

中文不能直接转为base64编码

//将中文转换为ASCII编码
var str = encodeURIComponent('你好,世界');

//将ASCII编码转换为base64编码
console.log( btoa( str ) );//JUU0JUJEJUEwJUU1JUE1JUJEJUVGJUJDJThDJUU0JUI4JTk2JUU3JTk1JThD


//将base64编码转换为ASCII编码
var base = atob('JUU0JUJEJUEwJUU1JUE1JUJEJUVGJUJDJThDJUU0JUI4JTk2JUU3JTk1JThD');

//将ASCII编码转换为中文
console.log( decodeURIComponent( base ) );//你好,世界

我们可以封装为两个函数,这样转码会方便多了

function b64Encode(str) {
  return btoa(encodeURIComponent(str));
}

function b64Decode(str) {
  return decodeURIComponent(atob(str));
}

b64Encode('你好') // "JUU0JUJEJUEwJUU1JUE1JUJE"
b64Decode('JUU0JUJEJUEwJUU1JUE1JUJE') // "你好"

7月16日

String对象

将其他数据流类型转换为字符串
String(true) // "true" 将布尔转换为字符串
String(12345) // "12345" 将数字转换为字符串
String([]) //'' 将数组转化为字符串,但功能很弱

String.fromCharCode

将一个或者多个unicode码点转换为好识别的字符串

String.fromCharCode() // ""
String.fromCharCode(97) // "a"
String.fromCharCode(104, 101, 108, 108, 111)
// "hello"

字符串的长度
'abc'.length 

字符串长度只读,不可写。

String实例的方法

charAt 返回指定索引值的字符
var str='今天的天气怎么样?';
console.log( str[0], str.charAt(0) );//今 今 

charCodeAt 返回指定索引值的unicode码点
'abc'.charCodeAt(1) // b 的nicode码点是 98

concat 拼接字符串,类似于加号。

连接过后产生新字符串

var a = '你好';
var b = '张三';

console.log( a + b );
console.log( a.concat(b) );
console.log( a.concat(b,'李四','王五') );

slice 从指定的位置,分割字符串。

位置计算规则

  • 位置从0开始计算,大于等于起始位置,小于结束位置。
  • 如果位置的值是负数,则倒着数,比如-1为倒数第1个,分割出一个新字符串。
  • 省略结束位置,表示截取到末尾

语法

字符串.slice(起始位置, 结束位置);

slice小示例

var str = '你今天的心情好吗?';

//1. 始终截取第1个字符串
console.log( str.slice(0,1) );

//2. 始终截取最后1个字符串
console.log( str.slice(-1) );

//3. 始终截取倒数3个字符
console.log( str.slice(-3) );

//4. 始终截取首尾单个字符串。
console.log( str.slice(0,1)+str.slice(-1) );

//5. 将字符串分割指定的段数。 你今、天的、心情、好吗、?

var total = str.length; //总字符数
var perPart = 5; //每段分2个字符
var totalPart = Math.ceil(total/perPart);  //上舍入 总段数

for(var p=0;p<totalPart;p++){
  var start = p * perPart;//4
  var end = start + perPart;//6
  console.log( str.slice( start, end ) );//2 4
}


substring 分割数组

slice方法很相像。它的第一个参数表示子字符串的开始位置,第二个位置表示结束位置。
如果省略第二个参数,则表示子字符串一直到原字符串的结束。

注意:substring的参数不支持负数

substr 截取字符串

语法

起始位置支持负数,如果是负数,那么倒着数,比如-1就是倒数第一个。

字符串.substr(起始位置,截取字符的长度);

var str = '你今天的心情好吗?';
console.log(  str.slice(4,6) );//起始位置 结束位置
console.log(  str.substr(4,2) );//起始位置 截取长度

indexOf 查找字符第一次出现的位置(索引值)

如果指定的字符不存在,则返回-1

语法

字符串.indexOf('要查找的字符','起始位置,默认是0')

示例

var str = 'xianweb@qq@.com';
var index = str.indexOf('@')+1;//8
console.log(str.indexOf('@', index));//查找第二个@

lastIndexOf 查找字符最后一次出现的位置(索引值)

如果指定的字符不存在,则返回-1

var str = 'xianweb@qq@.com';
console.log(str.lastIndexOf('@') );//查找第二个@

示例

var url = 'http://www.baidu.com/product/index.html';

//1. 截取网址文件名部分 index.html
console.log( url.slice(url.lastIndexOf('/')+1) );

//2. 截取网址主机和域名部分 www.baidu.com
var index = url.indexOf('//')+2;
console.log( url.slice( index, url.indexOf('/', index )  ) );

var filename = 'images/banner.jpg';

//1. 提取出文件扩展名 jpg
console.log( filename.slice(filename.lastIndexOf('.')+1) );

split 将字符串按指定字符转换为数组

返回的是数组,split支持''空字符串参数,howmany数值,返回所需数组最大长度。

语法

'苹果|桔子|香蕉'.split('|', howmany);//['苹果','桔子','香蕉']


示例

var filename = 'images/banner.jpg';
var arr = filename.split('.');
console.log(arr[arr.length-1]);

console.log( '苹果|桔子|香蕉'.split('|', 2) );//['苹果','桔子']

console.log('苹果桔子'.split('') );//['苹','果','桔','子']

toLowerCase toLocaleLowerCase 转换小写 转换本地小写

toLocaleLowerCase通常是你自己在原型链取同名函数覆盖,自定义自己的转换规则

console.log( 'ASADSADW'.toLowerCase()  );//asadsadw
console.log( 'ASADSADW'.toLocaleLowerCase()  );//asadsadw

推荐使用:toLowerCase

toUpperCase toLocaleUpperCase 转换小写 转换本地小写

toLocaleUpperCase通常是你自己在原型链取同名函数覆盖,自定义自己的转换规则

console.log( 'asadsadw'.toUpperCase()  );//ASADSADW
console.log( 'asadsadw'.toLocaleUpperCase()  );//ASADSADW

推荐使用:toUpperCase

repeat 将字符重复指定的次数 es6
//1. 替换手机中间部分为*号
var mobile = '18512345168';

var start = mobile.slice(0,3);
var end = mobile.slice(-4);
// console.log( start + '*'.repeat(4) + end );

//2.圆点自动补齐
var arr = ['abc','abcde','ssds','wewe','sds','fgf','fgf','fgf','fgf','fgf','fgf','fgf','fgf','fgf','fgf','fgf','fgf'];

//总长度
var total = 30;

//补齐符
var symbol = '.';

//自动补齐
for(var i=0;i<arr.length;i++){
  var num = total - arr[i].length - String(i+1).length;
  console.log( arr[i] + symbol.repeat(num) + (i+1) );
}

includes 查找是否包含某个字符串 es6

返回真和假

console.log('xianweb@qq.com'.indexOf('#'));//-1
console.log('xianweb@qq.com'.indexOf('@'));//7

console.log('xianweb@qq.com'.includes('#'));//false
console.log('xianweb@qq.com'.includes('@'));//true

replace 字符串替换

不使用正则替换,只能单次,并且区分大小写

语法

'字符串模板'.replace('被搜索的字符','用来替换的字符');

//示例
'hello'.replace('l','*');

//结果 he*lo

match、search 都是依赖使用正则表达式替换
localeCompare 比较两个字符的大小

字符串数字比较,

返回-1,前面的字符小于后面字符
返回0,前面的字符等于后面字符
返回1,前面的字符大于后面字符

var a = '1';
var b = '2';
console.log( a.localeCompare(b) ); // -1

var c = '1';
var d = '1';
console.log( c.localeCompare(d) ); // 0

var e = '2';
var f = '1';
console.log( e.localeCompare(f) ); // 1

字母比较

var a = 'a';
var b = 'b';
console.log( a.localeCompare(b) ); // -1

var c = 'a';
var d = 'a';
console.log( c.localeCompare(d) ); // 0

var e = 'b';
var f = 'a';
console.log( e.localeCompare(f) ); // 1

注意:中文是比较是采用unicode数字码比较,不是想要的效果。

Array数组对象

定义数组
  1. 构造函数方式定义
var arr = new Array();
arr[0] = '苹果';
arr[1] = '桔子';
console.log( arr );

  1. 构造函数参数赋值快速定义
var arr = new Array('苹果','桔子');
console.log( arr );

  1. 使用中括号简写定义(推荐)
var arr = ['苹果','桔子'];
console.log( arr );

数组的成员可以是任意数据类型
var arr = [ 
  1,
  true, 
  '苹果',
  undefined,
  null, 
  ['张三'],
  function(){
    return 10;
  },
  {
    id:10
  }
];

数组的长度

数组的长度可读可写

数组.length

示例

var arr = [];
arr.length = 10;//写
console.log( arr.length );//读 10

注意: 当我们使用length设置长度,如果产生空位,空位的值是undefined

var arr = ['a','b','c','d'];
arr.length = 2;
console.log( arr ); // ["a", "b"]
console.log( arr[100] );// 读 undefined
console.log(arr.length );// 2

数组和数组不能直接用加号相加,这样数组会自动转换为字符串

var arr1 = ['a','b'];
var arr2 = ['c','d'];

//隐式转换为字符串 ['a','b'] =>  a,b
console.log( arr1 + arr2 ); // a,bc,d

var str = '苹果,桔子,香蕉';
var arr = str.split(','); // ['苹果','桔子','香蕉']
console.log( arr );
console.log(  arr + '' ); // 苹果,桔子,香蕉

当给数组的length设置为0,那么就相当于清空数组。

//清空数组
console.log( ['苹果','桔子','香蕉'].length=0 );//[]

多维数组

如果数组的成员还是数组,就形成了多维数组。

var a = ['苹果','桔子']; //一维数组
var b = [ 
  ['苹果','桔子'], 
  ['青菜','肉'] 
]; 
//二维数组

//数组和对象相接口,非常类似于数据库记录集,类似于一个标准表格数据。
var data = [
  { id: 1, title: '标题1', author:'张三' },
  { id: 2, title: '标题2', author:'张三' },
  { id: 3, title: '标题3', author:'张三' }
]

数值键名会自动转换为字符串
//类数组对象
var obj = {
  0: '苹果',
  '1': '桔子',
  length: 2
}
console.log( obj[0], obj[1], obj['1'] );

数组的读写删操作

数组读取

var arr = ['苹果','桔子'];
console.log( arr[1] );

数组写入新成员

var arr = ['苹果','桔子'];
arr[2] = '香蕉'; //写入一个成员
console.log( arr );// ["苹果", "桔子", "香蕉"]

delete 数组删除成员

var arr = ['苹果','桔子','香蕉'];
delete arr[1];
console.log( arr );// ['苹果',empty,'香蕉']

注意:delete 删除数组成员并不会改变数组长度,而只是形参一个空位。

in 运算符

检查某个键名是否存在,适用于对象,也适用于数组。

var arr = ['苹果','桔子','香蕉'];
console.log( 0 in arr  );//true
console.log( 5 in arr  );//false

for…in 循环和数组的遍历
var arr = ['苹果','桔子','香蕉'];

//for 循环
for(var i=0;i<arr.length;i++){
  console.log( arr[i] );
}

//while 循环
var i=0;
while(arr[i]){
  console.log(arr[i]);
  i++;
}

//do while 循环
var i=0;
do{
  console.log(arr[i]);
  i++;
} while( i < arr.length );

//for in 循环 (不推荐)
for(var i in arr){
  console.log(arr[i]);
}

// forEach 高阶函数
arr.forEach( function( a,i,arr ){
  console.log(a,i,arr);
})


2019-7-17

forEach循环

forEach 不一定是数组特有的方法 不会循环空成员,比如:

var arr = [1,,,2,3,undefined];
//上面的数组用forEach循环,只会输出1,2,3,undefined

forEach 没有返回值,也就是内部加return没有返回值。

类数组对象转换为真正的数组对象

语法

var arr = Array.prototype.slice.call(类数组对象);

示例

//类数组对象
var obj = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
};

//类数组转化为真正的数组
var arr = Array.prototype.slice.call(obj);

//因为已经数组了,所有使用forEach无妨
arr.forEach(function( item ){
  console.log( item );
})

因为字符串也是类数组,同样可以用上面的方法转成真正的数组

var str = '今天的天气不错';
var arr = Array.prototype.slice.call( str );
//输出 ["今", "天", "的", "天", "气", "不", "错"]

Array.isArray 检测是否是数组
var arr = ['a','b'];
console.log(  Array.isArray(arr)  );//true

对象

什么是对象?简单说,对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合。

对象是变量的集合(容器)

键值可以是任何数据类型

对象的属性之间用英文逗号分割

书写对象过程中常见问题

对象所有的键名都是字符串,所以加不加引号都可以,因为系统内部会转换为字符串

es6中引入了一种数据类型,叫symbol,它确保键名是唯一的,也可以使用symbol作为键名。

//问题1:尾部原则上不需要加英文逗号,但现代浏览器都能忽略
var obj = {
  foo: 'Hello',
  bar: 'World',
};

//问题2:键名和键值之间是英文冒号
var obj = {
  foo= 'Hello',
  bar= 'World',
};

//问题3:属性之间用英文逗号分割
var obj = {
  foo: 'Hello';
  bar: 'World';
};

//问题4:使用了中文的逗号
var obj = {
  foo: 'Hello',
  bar: 'World'
};

变量是传值传递,数组和对象因为是引用类型,所以它们是传址传递

//1.变量 => 传值传递
var a = 10;
var b = a;//10
a = 20;
console.log( a, b );// a = 20  b = 10

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//2. 对象 => 传址传递
var c = {
  name: '张三'
}

var d = c;

c.name = '李四';//对象写入

console.log( c, d );
//c = { name:'李四' }
//d = { name:'李四' }

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//3. 数组 => 传址传递
var a = ['苹果','桔子'];

var b = a;

a[1] = '香蕉';//数组写入

console.log( a, b );
//a = ['苹果','香蕉']
//d = ['苹果','香蕉']

表达式还是语句?

当对象的大括号{在首行首字母位置,如下,js引擎解析会产生歧义,为了避免下面的歧义,js规定,大括号出现在首行首字母位置,一律认为是语句,如果要解析为表达式,则需要用圆括号包裹对象即可。

//会被认为是语句
{
  name:'张三'
}

//会被认为是表达式
({
  name:'张三'
})

//表达式
var obj = {
  name:'张三'
}

eval函数

eval 函数会将字符串的内容当成表达式解析,注册改函数容易引起安全隐患,和性能开销比较大。

//1. eval 四则运算
var str='1*5+5';
console.log( str,  eval( str ) );

//2. eval 直接解析变量
var a = 10;
var str = 'a=30;';
console.log( str,  eval( str ) );


//3. eval 直接解析数组引用
var a = ['苹果','桔子'];
var str = 'a[1]';
console.log( str,  eval( str ) );

因为eval会带来安全问题和性能开销比较大,通常一些框架会屏蔽此函数,具体如下:

window.eval = null;

对象属性的操作

读取属性

  1. 通常点的形式读取
var obj = {
  name: '张三',
  'first-name': '李四',
  1: 'abc'
}
console.log(obj.name);//张三

  1. 通常中括号的形式读取
var obj = {
  name: '张三',
  'first-name': '李四',
  1: 'abc'
}
console.log(obj['name']);//张三
console.log(obj['first-name']);//李四
console.log(obj[1]);//abc

当键名是变量时,那么点调用的方式无效,应该使用中括号

var str = 'name';
var obj = {
  name: '张三',
  age: 20
}
str = 'age';
console.log(  obj[str] );// 20
console.log(  obj.str );//undefined,需要用[]

属性的赋值

点运算符和方括号运算符,不仅可以用来读取值,还可以用来赋值。

// 声明时直接填入属性
var person1 = {
  name: '张三',
  age: 20
}
console.log( person1 );

// 先声明一个对象,后填入属性
var person2 = {};
person2.name = '张三';
person2['age'] = 20;

console.log( person2 );

//对象比较是比较内存地址,不是值
console.log( person1 == person2 );//false

对象比较是比较内存地址,如下

// 声明时直接填入属性
var a = {
  name: '张三',
  age: 20
}
var b = a;//b等于a相当于b的内存地址指向了a
console.log( a == b  ); //true

中括号包裹键名,会起到解析为变量的作用

// 不加中括号
var str = 'name';
var a = {
  str:'张三',
  age: 20
}
console.log( a );//{ str:'张三',age:20 }

// 加中括号,会解析为变量
var str = 'name';
var a = {
  [str]:'张三',
  age: 20
}
console.log( a );//{ name:'张三',age:20 }

变量取名慎用name,在某些情况下会产生冲突。

Object.keys查看所有属性

Object.keys方法会将数组中所有的键名,放入到一个新数组中,返回数组。

当键名是symbol命名时,Object.keys会跳过symbol命名属性

var str = Symbol();
var a = {
  [str]: '张三',
  age: 20
}
var keys = Object.keys( a );
console.log( keys  );//['age']

//采用symbol数据类型命名的键名调用
var str = Symbol();
var a = {
  [str]: '张三',
  age: 20
}
console.log(a[str]);//张三

delete 命令删除对象属性
var str = Symbol();
var a = {
  [str]: '张三',
  age: 20
}
delete a[str];//删除symbol类型键名
delete a.age;//删除string类型键名
console.log( a );//{}

注意,删除一个不存在的属性delete不报错,而且返回true

var obj = {};
console.log( delete obj.name);//true

只有一种情况,delete命令会返回false,那就是该属性存在,且不得删除。

//1. 创建新对象创建新属性
var obj = Object.defineProperty( {},'name', {
  value: '张三',
  configurable: true
})
console.log( delete obj.name, obj );  //false


//2. 在原有对象基础上扩展新属性
var person = {
  name: '张三'
}
var obj = Object.defineProperty( person, 'age', {
  value: 56, //键值
  configurable: true, //configurable表示是否能被delete删除,false不能删除
  enumerable: true //enumerable表示是否能被Object.keys枚举,true可枚举
})
console.log( delete person.age, Object.keys(obj) );

delete命令只能删除对象本身的属性,无法删除继承的属性

var obj = {};
delete obj.toString // true
obj.toString // function toString() { [native code] }

in 运算符

in 运算符用于检查对象是否包含某个属性
in 运算符 继承的属性 也可以检测到,返回true
in 运算符 是浅查找属性,只支持一级属性验证。

var person = {
  name:'张三',
  info: {
    xueli: '本科'
  }
}
console.log( 'name' in person ); //true 
console.log( 'xueli' in person ); //false
console.log( 'xueli' in person.info ); //true

var obj = {
  name:'张三'
}
Object.defineProperty( obj, 'age', {
  value: 30,
  enumerable: false
})

console.log( obj );

console.log( 'name' in obj ); //true
console.log( 'toString' in obj ); //true
console.log( 'age' in obj );// true

defineProperty

for in 循环对象
  • for in 循环会跳过继承属性,比如toString
  • for in 循环会跳过不可枚举属性,比如设置enumerable: false
var obj = {
  name:'张三'
}
Object.defineProperty( obj, 'age', {
  value: 30,
  enumerable: true
})
for(var k in obj){
  console.log( k, obj[k] );
}

将对象的键值提取出来,装入一个新数组,模仿Object.keys功能

var index = 0;
var arr = []
for(var key in info){
  arr[index] = info[key];
  index++;
}
console.log( arr );

经典面试题

//查找字符重复的次数
var str = '一d你de你sqssd';

//对象方式
var info = {};
for( var i=0; i<str.length; i++ ){
  var val = str[i];
  if( val in info ){
    info [ val ] ++ ;
  }else{
    info [ val ] = 1;
  }
}
console.log( info );//{一: 1, d: 3, 你: 2, e: 1, s: 3, …}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~···

//查找重复最多的次数
var index = 0;
var firstVal = 0;

for(var key in info ){
  if( index == 0 ){
    firstVal = info[key];
  }else{
    if( firstVal < info[key]){
      firstVal = info[key];
    }
  }
  index++;
}
console.log( firstVal );// 3 

数组的方法

实例方法

变异和非变异的理解

  • 变异的方法:破坏了原始数组
  • 非变异的方法:不会破坏原始数组,产生新数组
  1. valueOf() 数组原始值 toString()将数组转化为字符串
var arr = ['苹果','桔子'];
console.log( arr.valueOf() ); // [1, 2, 3]
console.log( arr.toString() ); // 1, 2, 3
console.log( arr+'' ); // 隐式转换 1, 2, 3
console.log( arr.join(',') );// 1, 2, 3

  1. join() 将数组按指定的字符转换为字符串

返回新的字符串,不会破坏原始数组,非变异方法。

如果不提供参数,默认用逗号分隔。

var arr = ['苹果','桔子'];
console.log( arr.join('') ); //苹果桔子
console.log( arr.join('@') ); //苹果@桔子
console.log( arr.join() ); //苹果,桔子

var str = Array.prototype.join.call('hello', '-');
console.log( str );//h-e-l-l-o

  1. push() 向数组的尾部追加成员

返回数组最新的长度,该原始数组已经被添加新成员,是变异的方法。

var arr = ['苹果','桔子'];
arr[2] = '菠萝';
var result = arr.push('香蕉'); //执行过后返回数组最新的长度

console.log( result ); // 4
console.log( arr ); // ["苹果", "桔子", "菠萝", "香蕉"]

var arr = ['苹果','桔子'];
arr.push('香蕉','菠萝'); //一次性追加2个
arr.push( ['生菜','西红柿'] ); //追加一个数组
console.log( arr );// ['苹果','桔子','香蕉','菠萝', ['生菜','西红柿'] ]

  1. unshift() 向数组的开头追加成员

返回数组最新的长度,该原始数组已经被添加新成员,是变异的方法。

和 上面的push方法几乎一样,只是插入成员的位置不同。

var arr = ['苹果','桔子'];
arr.unshift('香蕉','菠萝'); //一次性追加2个
console.log(arr);//["香蕉", "菠萝", "苹果", "桔子"]

  1. pop() 删除数组最后一个成员

返回被删除成员的值,该方法会破坏原始数组,是变异的方法。

var arr = ['苹果','桔子'];
var result = arr.pop();

console.log( result );// 桔子
console.log( arr );// ['苹果']

  1. shift() 删除数组开头第一个成员

返回被删除成员的值,该方法会破坏原始数组,是变异的方法。

var arr = ['苹果','桔子'];
var result = arr.shift();

console.log( result );// 苹果
console.log( arr );// ['桔子']

  1. concat()

用于多个数组的合并,合并出一个新数组,不会破坏原始数组,是非变异的方法

var a = ['苹果','桔子'];
var b = ['芒果','西瓜'];
var arr = a.concat( b ); 
console.log( arr );// ['苹果','桔子','芒果','西瓜']


push()concat()的区别:

  1. push 返回数组新长度,concat 返回新数组
  2. push 变异的方法,concat 非变异的方法
  3. push 添加的是数组,会产生多维数组,concat 不会产生多维数组
  4. reverse() 反转数组

反转数组,返回已经破坏过后的原始数组,是变异的方法

var arr = [1,2,3];
arr.reverse();
console.log( arr ); // [3,2,1]

示例

//分步操作
var str1 = '今天的天气不错';
var arr = str1.split('');
arr.reverse();
var str1 = arr.join('');
console.log( str1 );

//链式操作
var str2 = '今天的天气不错'.split('').reverse().join('');
console.log( str2 );

  1. splice() 集删除、添加、修改功能的数组方法

该函数集删除、添加、修改功能的数组方法,会破坏原始数组,变异的方法。

起始位置,数值,支持负数,表示从后往前数。

删除的个数,数值,必填,如果设置为0,表示不删除。

返回被删除的数组

语法

数组.splice(起始位置,删除的个数, 新成员值1,新成员值2,....);

splice() 删除操作

//删除操作
var arr = ['苹果','桔子','芒果'];
arr.splice(1,1);
console.log( arr );

//1. 清空数组 splice
var arr1 = ['苹果','桔子','芒果'];
arr1.splice(0);//第2个参数省略,代表数组最大的长度
console.log(arr1);

//2. 始终删除第一个成员 splice
var arr2 = ['苹果','桔子','芒果'];
arr2.splice(0,1);
console.log(arr2);

//3. 始终删除最后一个成员 splice
var arr3 = ['苹果','桔子','芒果'];
arr3.splice(-1,1);
console.log(arr3);

//4. 始终只取数组最后两个成员 splice
var arr4 = ['苹果','桔子','芒果'];
arr4.splice(0, arr4.length-2);
console.log(arr4);

splice() 添加操作

注意:添加是在起始位置的前面追加

//添加操作

//1. 在桔子后追加香蕉1个成员
var arr1 = ['苹果','桔子','芒果'];
arr1.splice( 2, 0, '香蕉' );
console.log( arr1 );

//2. 在苹果后面追加火龙果、樱桃2个成员
var arr2 = ['苹果','桔子','芒果'];
arr2.splice( 1, 0, '火龙果','樱桃' );
console.log( arr2 );


//3. 在芒果后面追加数组['哈密瓜','西瓜']
var arr3 = ['苹果','桔子','芒果'];
arr3.splice( arr3.length, 0, ['哈密瓜','西瓜'] );//类似push方法
console.log( arr3 );

splice 修改操作,采用先删后加原则

//1. 在桔子修改为香蕉
var arr1 = ['苹果','桔子','芒果'];
arr1.splice(1,1,'香蕉');
console.log( arr1 );

//2. 在桔子修改为香蕉,芒果修改为西瓜
var arr2 = ['苹果','桔子','芒果'];
arr2.splice(1,2,'香蕉','西瓜');
console.log( arr2 );

  1. slice() 分割数组

slice 按照起始位置和结束位置分割出一个新数组,它不会破坏原始数组,是非变异的方法,返回新数组。

起始位置,数值,可以是负数,表示从后开始算,-1 表示倒数第一个

结束位置,数值,可以是负数,表示从后开始算,-1 表示倒数第一个

省略结束位置,表示到数组末尾(相当于数组的长度)。

语法

数组.slice(起始位置,结束位置)

示例

var arr = ['苹果','桔子','香蕉','菠萝','西瓜'];

//1. 提取第一个成员
console.log( arr.slice(0,1) );

//2. 提取第桔子和香蕉
console.log( arr.slice(1,3) );

//3. 始终提取最后一个成员
console.log( arr.slice(-1) );

//4. 始终提取除第一个和最后一个成员
console.log( arr.slice(1,-1) );

//5. 始终提取最后两个成员
console.log( arr.slice(-2) );

示例求学生三门成绩的总分、平均分、最低分、最高分

    <style>
        * {
          margin: 0;
          padding: 0;
          box-sizing: border-box;
        }
        body{
          padding: 20px;
        }
        table{
          border-collapse:collapse;
          border: 1px solid #ccc;
        }
        th,td{
          padding: 10px;
        }
        tbody tr:nth-child(odd) td{
          background-color: #eee;
        }
      </style>
  <script>
  var data = [
    { id: 1, name:'张三', score: [ 78, 88, 75] },
    { id: 2, name:'李四', score: [ 66, 79, 85] }
  ];

  var temp = `<table cellspacing="0" border="1">
    <thead>
      <tr>
        <th>学号</th>
        <th>姓名</th>
        <th>语文</th>
        <th>数学</th>
        <th>外语</th>
        <th>总分</th>
        <th>平均分</th>
        <th>最低分</th>
        <th>最高分</th>
      </tr>
    </thead>
    `;
  
  for(var i=0;i<data.length;i++){

    var score = data[i].score;
    var sum = score.reduce( function( prev, next ){ 
      return prev + next;
    });//总分
    var min = Math.min.apply( null, score );//最低分
    var max = Math.max.apply( null, score );//最高分
    var avg = (sum/3).toFixed(1);//平均分

    temp += `
    <tbody>
      <tr>
        <td>${data[i].id}</td>
        <td>${data[i].name}</td>
        <td>${score[0]}</td>
        <td>${score[1]}</td>
        <td>${score[2]}</td>
        <td>${sum}</td>
        <td>${avg}</td>
        <td>${min}</td>
        <td>${max}</td>
      </tr>
    </tbody>
    `;
  }

  temp += '</table>';

  document.write( temp );

  var arr = [2,3,5,6,8,9];
  var num = Math.max.apply( null, arr );
  

  console.log( num );

  </script>

分页四要素
  1. 当前页 page
  2. 总个数 total
  3. 每页多少个 perpage
  4. 总页数 totalpage

sort() 对数组成员进行排序,默认是按照字典顺序排序,排序后,原数组将被改变,变异的方法。

//数字排序
var arr = [ 2,6,1,9, 4];
arr.sort();
console.log( arr ); // [1, 2, 4, 6, 9]

//数字排序不正确,是按字符串排序
var arr = [ 2, 6, 1, 9, 4, 12, 24];
arr.sort();
console.log( arr ); // [1, 12, 2, 24, 4, 6, 9]

//字符串排序
var arr = [ 'b', 'a', 'c', 'd', 'ab', 'bc'];
arr.sort();
console.log( arr ); //["a", "ab", "b", "bc", "c", "d"]


以上排序有局限性,比如数字它是字符来排序的,而且也不方便升序、降序切换

我们可以通过设置一个回调函数使得排序更加精确

语法

数组.sort( function( prev, next ){
  return prev - next;
})

数值排序示例

//数值排序
var arr = [ 2, 6, 1, 9, 4, 12, 24];
var flag = -1;// 1 为升序  -1 降序
arr.sort( function( a, b ){
  return ( a - b ) * flag;
});
console.log( arr ); 

字符串排序

//字符串排序
var arr = [ 'b', 'a', 'c', 'd', 'ab', 'bc' ];
var flag = 1; // 1 为升序  -1 降序

arr.sort( function( a, b ){
  return a.localeCompare( b ) * flag;
})

console.log( arr ); 

数组也可以用来替代条件语句输出

var day = new Date().getDay();//0 - 6
var week = ['日','一','二','三','四','五','六'];
console.log('今天是星期' + week[day] );

map() 将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回,它是非变异的方法。

var arr = [ 1, 2, 3, 4, 5];
var newArr = arr.map(function( item ){
  return item * 2;
})

console.log( newArr );// [ 2, 4, 6, 8, 10 ]

//取出偶数成员
var arr = [ 1, 2, 3, 4, 5];
var newArr = arr.map(function( item ){
  if(item % 2 == 0) {
    return item;
  }
})
console.log( newArr );//[undefined, 2, undefined, 4, undefined]

//map中this指向第二个参数,下图也就是arr数组

var arr = ['a', 'b', 'c'];
var newData= [0, 2].map(function ( item ) {
  return this[ item ];
}, arr);

console.log( newData ); // ['a','c']

forEach()map方法很相似,也是对数组的所有成员依次执行参数函数,forEach方法不返回值,只用来操作数据。

示例

var arr = [ 1, , 3, , 5];
arr.forEach(function( item, index, arr ){
  console.log( item, index, arr );
})

forEachmap的区别

  • forEach 没有return返回值,它会跳过空位
  • mapreturn返回值,它会跳过空位

filter() 方法用于过滤数组成员,满足条件的成员组成一个新数组返回,非变异的方法。

//查找出不及格的分数
var score = [ 50, 70, 58, 62, 80];
var newScore = score.filter( function(item){
  return item < 60;
})

console.log( newScore );//[50, 58]

//也可以利用排除法删除
var arr = [ 50, 70, 58, 62, 80];
arr = arr.filter( function(item){
  return item != 62;
})
console.log( arr );//[ 50, 70, 58, 80];

html5 本地存储

本地存储,所存储的值都是字符串类型,所以如果是对象或者数组,我们需要转换为字符串。

可以采用JSON.stringify将对象或者数组转换为字符串类型

数组转字符串示例

var arr = [
  {id:1,name:'张三'}
]
var str = JSON.stringify( arr );// 字符串数组 

可以采用JSON.parse将字符串类型转换为对象或者数组

字符串转数组示例

var str = '[{"id":1,"name":"张三"}]';
var arr = JSON.parse( str );// 数组 [{id:1,name:'张三'}]

  • sessionStorage 会话存储

关闭当前窗口,存储的信息会立即消失

  1. sessionStorage.setItem(键名,键值) 设置存储
  2. sessionStorage.getItem(键名) 获取存储值
  3. sessionStorage.removeItem(键名) 移除存储值
  4. sessionStorage.clear() 清空所有会话存储

会话临时存储演示示例

<button onclick="saveData()">存储</button>
<button onclick="getData()">获取</button>
<button onclick="removeData()">移除name</button>
<button onclick="clearData()">清空</button>

<script>

function saveData(){
  sessionStorage.setItem('name','张三');
  sessionStorage.setItem('age','24');
  sessionStorage.setItem('sex','24');
  alert('存储成功!');
}
function getData(){
  alert( sessionStorage.getItem('name') );
}
function removeData(){
  alert( sessionStorage.removeItem('name') );
}
function clearData(){
  sessionStorage.clear();
  alert('清空了!');
}
</script>

  • localStorage 永久存储

本地永久存储,就算你关闭了浏览器,数据依然存在,除非你手动清空掉它

  1. localStorage.setItem(键名,键值) 设置存储
  2. localStorage.getItem(键名) 获取存储值
  3. localStorage.removeItem(键名) 移除存储值
  4. localStorage.clear() 清空所有会话存储
<button onclick="saveData()">存储</button>
<button onclick="getData()">获取</button>
<button onclick="removeData()">移除name</button>
<button onclick="clearData()">清空</button>

<script>
function saveData(){
  localStorage.setItem('name','张三');
  localStorage.setItem('age','24');
  localStorage.setItem('sex','24');
  alert('存储成功!');
}
function getData(){
  alert( localStorage.getItem('name') );
}
function removeData(){
  alert( localStorage.removeItem('name') );
}
function clearData(){
  localStorage.clear();
  alert('清空了!');
}
</script>

在早期浏览器不支持html5本地存储,那么使用cooike存储,cookie存储大小有限制,大小4KB,存储数量也有限制,一个网站大概50多个,cookie它一个时效性,我们可以给cookie设置一个保存时间,怎么删除cookie呢?设置cookie的时间为过去时间。

  • 创建cookie

不加过期时间的话,属于会话存储,关闭浏览器数据消失

cookie是明文存储,不可以直接把重要信息(密码)存入cookie

//关闭浏览器,会被自动回收
document.cookie="username=龚贤";

//加上过期时间
document.cookie="username=龚贤;expires=Thu, 18 Dec 2043 12:00:00 GMT;";

//加上使用路径权限,/代表当前根目录
document.cookie="username=龚贤;expires=Thu, 18 Dec 2043 12:00:00 GMT;path=/";

  • 获取cookie

设置cookie和获取cookie是同一个方法

console.log( document.cookie );//username=龚贤

  • 删除cookie

删除cookie,只需要设置一个过期的时间,删除cookie一般是需要关闭整个浏览器之后生效。

注意:chrome需要以服务器的方式访问,cookie才能生效,本地静态方式访问无效(file://)

注意:有时候设置了关闭浏览器后cookie才能清除

示例

  
  <button onclick="setItem()">存储cookie</button>
  <button onclick="getItem()">读取cookie</button>
  <button onclick="delItem()">删除cookie</button>

  <script>
  function setItem(){
    document.cookie="username=老三&age=33&sex=男;expires=Thu, 18 Dec 2043 12:00:00 GMT;path=/";
    alert('存储成功!');
  }
  function getItem(){
    var str = document.cookie; // username=老三&age=33&sex=男
    var params = new URLSearchParams( str );
    console.log( params.get('username') );//老三
    console.log( params.get('age') ); //33
    console.log( params.get('sex') ); // 男
  }
  function delItem(){
    //注意chrome浏览器
    document.cookie="; expires=Thu, 01 Jan 1970 00:00:00 GMT";
    alert('删除成功!');
  }
  </script>

cookiesessionStroagelocalStroage 之间的区别

存储大小:

  • cookie数据大小不能超过4k。
  • 本地存储有存储大小的限制,可以达到5M或更大。

生命周期:

  • cookie 设置过期时间之前一直有效,即使窗口或浏览器关闭
  • localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据
  • sessionStorage 数据在当前浏览器窗口关闭后自动删除

some() 只要一个成员的返回值是true,那么结果就是true

该方法返回布尔类型(true/false)

var data = [
  { id:1,name:'张三'},
  { id:2,name:'李四'}
]

var result = data.some(function( item ){
  return item.name == '张三';
})

console.log( result );//true

every() 每一个成员都返回值是true,那么结果就是true

该方法返回布尔类型(true/false)

//示例一
var data = [
  { id:1,name:'张三',score: 70},
  { id:2,name:'李四',score: 56}
]
var result1 = data.every(function( item ){
  return item.score >= 60 ;
})

console.log( result1 ); //false,因为李四未及格,所以返回假

//示例二
var data = [
  { id:1,name:'张三',score: 70},
  { id:2,name:'李四',score: 66}
]
var result2 = data.every(function( item ){
  return item.score >= 60 ;
})
console.log( result2 );// true,每个成员都及格了,所以返回真


reduce() 和 reduceRight()

reduce方法和reduceRight方法依次处理数组的每个成员,最终累计为一个值。它们的差别是,reduce是从左到右处理(从第一个成员到最后一个成员),reduceRight则是从右到左(从最后一个成员到第一个成员),其他完全一样。

语法

数组.reduce( function(数组上一个成员,数组下一个成员){

},初始值默认为0)

//累积求和
var arr = [1, 2, 3, 4, 5];
var sum = arr.reduce(function (a, b) {
  return a + b;
})
console.log( sum );//15

//数组对象形式的求和
var data = [
  { id:1,name:'张三',score: 10},
  { id:2,name:'李四',score: 20},
  { id:3,name:'王二',score: 30}
]

var score = data.reduce( function( prev, next ) {
  return prev + next.score;
},0);
console.log( score ); //60

indexOf() 和 lastIndexOf()

数组也有indexOf方法,注意indexOf方法执行严格匹配,没有找到返回-1,返回数组成员的索引值

var arr = [ 1,'2','A','b'];
console.log( arr.indexOf('1') );// -1 执行===操作
console.log( arr.indexOf('2') );// 1 返回数组成员索引值
console.log( arr.indexOf('a') );// -1 区分大小写
console.log( arr.indexOf('b') );// 3 返回索引值

includes es6 返回数组成员是否包含某个字符,若包含返回true,否则返回false

var arr = [ 1,'2','A','b'];
console.log( arr.includes('1') );// false
console.log( arr.includes('2') );// true
console.log( arr.includes('a') );// false
console.log( arr.includes('b') );// true

Array.from 将类数组转换为真正的数组

var str = '今天好今热今!';

var arr1 = Array.prototype.slice.call( str );
console.log( arr1 );

var arr2 = str.split('');
console.log( arr2 );

var arr3 = Array.from( str );
console.log( arr3 );

new Set() 实例化一个类数组对象

//将字符串中重复的字符抛弃,最后返回一个类数组对象
var obj = new Set( str );
console.log( obj );

findIndex 查找数组的索引值

var data = [
  { id:1, name: '网易', star:'★★★' },
  { id:2, name: '搜狐', star:'★' },
  { id:3, name: '凤凰网', star:'★★★' }
];

var index = data.findIndex( function( item ){
  return item.id == 2;
})

console.log( index );//1

find 查找数组的对应成员

var data = [
  { id:1, name: '网易', star:'★★★' },
  { id:2, name: '搜狐', star:'★' },
  { id:3, name: '凤凰网', star:'★★★' }
];

var item = data.find( function( item ){
  return item.id == 2;
})

console.log( item );//{id: 2, name: "搜狐", star: "★"}

函数

函数是一段可以反复调用的代码块。函数还能接受输入的参数,不同的参数会返回不同的值。

函数是第一等公民

函数是本身也是一种值,类似字符串、数值、布尔。

函数的声明
  • 语句式声明
//函数语句式声明
function add( a, b ){
  return a+b;
}

  • 表达式声明
//函数表达式声明
var add = function( a, b ){
  return a+b;
}

  • 构造函数声明
var add = new Function( 'a', 'b','return a + b' );

示例

//语句式声明
function add( a, b ){
  return a+b;
}
console.log( add(1,5) );//6

//表达式声明
var add = function( a, b ){
  return a+b;
}
console.log( add(1,5) );//6

//构造函数声明
var add = new Function( 'a', 'b','return a + b' );
console.log( add(1,5) );//6

语句式声明和表达式声明有什么区别?

  • 语句式声明会自动提升
//函数会自动提升,不会报错
console.log( add(1,5) ); //6

//语句式声明
function add( a, b ){
  return a+b;
}

console.log( add(1,5) ); //6

  • 表达式声明不提升
//不会提升,致命错误
console.log( add(1,5) ); //Uncaught TypeError: add is not a function

//表达式声明
var add = function( a, b ){
  return a+b;
}
console.log( add(1,5) ); //6

构造函数声明可以用来解析字符串表达式,类似eval

console.log( eval('5+6*2') );//17

var fn = new Function( 'return 5+6*2' );
console.log( fn() );//17

函数的重复声明

代码是从上到下执行,后面的函数覆盖前面的函数,注意语句式声明函数会提升到代码顶部

//var 定义 会覆盖
var a = '111';
var a = '222';
console.log( a ); //会覆盖 222

//let 定义 不会覆盖
let b = 'aaa';
let b = 'bbb';

console.log( b );//报错,重定义错误

//const 定义 不会覆盖
const c = 'true';
const c = 'false';//报错,重定义错误

//函数 重定义 会覆盖
function add(){
  console.log('hello');
}

var add = function(){
  console.log('您好');
}

function add(){
  console.log('fine');
}

add(); //因为语句式声明会提升,表达式不提升,所以输出您好

圆括号运算符,return 语句和递归

  • 圆括号

调用函数时,要使用圆括号运算符。圆括号之中,可以加入函数的参数。

function add(x, y) {
  return x + y;
}

//调用函数加圆括号,在括号之中加入参数
add(1, 1) // 2

  • return 语句

return返回并且跳出函数,return是函数内部的语句。

returnbreak的区别

function echo(){
  for(var i=0;i<10;i++){
    if(i>5){
      return i; //跳出函数
      break; //跳出循环
    }
  }
  return 'hello';
}

console.log( echo() ); //6

return 是函数内部语句,必须有函数包裹

for( var i=0; i<10; i++ ){
  console.log( i );
  if(i>5){
    return;
  }
}
//报错,Uncaught SyntaxError: Illegal return statement

  • 函数递归

函数可以调用自身,这就是递归,递归一定要有个结束值(return),不然会形成死循环。

//因为没有结束条件,所以这是一个死循环
function fn( n ){
  console.log( n );
  fn( n+1 );
}
fn( 0 );//0 1 2 3 .....

//降序
function desc( n ){
  console.log( n );
  if( n == 0 ) return;
  desc( n-1 );
}

desc( 5 );//5 4 3 2 1 0

//升序
function asc( n ){
  console.log( n );
  if( n == 5 ) return;
  asc( n + 1 );
}

asc( 0 );//0 1 2 3 4 5


//阶乘
function fn( n ){
  // console.log( n );
  if( n == 0 ) {
    return 1;
  }else{
    return n * fn( n-1 );
  }
}

//斐波那契数列
function fib(num) {
  if (num === 0) return 0;
  if (num === 1) return 1;
  return fib(num - 2) + fib(num - 1);
}

fib(6) // 8

函数是第一等公民

JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。

  • 可以把函数赋值给变量
var add = function( a, b){
  return a+b;
}

  • 可以把函数赋值给对象的属性
var obj = {
  name: '张三',
  getAge: function(){
    return 30;
  }
}

  • 可以当作参数传入其他函数(作为参数时不能加括号)
//1. 将函数作为参数,并提取出来声明
function add( a, b ){
  console.log( a ); // function(){ }
  console.log( b ); // 5
  console.log( a() + b );//要加括号 需要执行
}

function fn(){
  return 10;
}

add( fn, 5 );//15

//2. 将函数作为参数声明
function add( a, b ){
  console.log( a() + b );
}

add( function(){
  return 10;
}, 5 );// 15


函数作为参数传入到函数内部,我们也叫这个函数(参数)回调函数

//仿照数组实例forEach
function forEach( arr, fn ){
  for(var i=0;i < arr.length;i++){
    fn( arr[i], i, arr );
  }
}

//调用
var arr = ['a','b','c','d'];
forEach( arr, function( item, index, arr){
  console.log( item, index, arr );
});

//仿照数组实例fliter
function filter( arr, fn ){
  var newArr = [];
  for(var i=0; i < arr.length; i++ ){
    var result = fn( arr[i], i, arr );
    if( result ){
      newArr.push( arr[i] );
    }
  }
  return newArr;
}
var newData = filter( data, function( item, index, arr ){
  return item.name=='张三';
});
console.log( newData );

函数名的提升

JavaScript 引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。所以,下面的代码不会报错。

f();
function f() {}

变量提升

//1
console.log( a ); // 报错,未定义


//2
console.log( a ); // undefined
var a;

//3
console.log( a ); // undefined
var a = 10;

//4
console.log( a ); // undefined
var a = 10;
console.log( a ); // 10


变量提升面试题

function foo( x ){
  if( x > 100 ) {
    var tmp = x - 100;
  }
  console.log( tmp ); 
}

foo( 50 ); // undefined

//以上代码变量会提升,等同于
function foo( x ){
  var tmp;
  if( x > 100 ) {
    tmp = x - 100;
  }
  console.log( tmp ); 
}

不能在条件语句中声明函数

不要在条件语句是声明函数,以下写法不推荐

var foo = false;
if ( foo ) {
  function fn() {
    console.log('hello');
  }
}

fn();//报错 fn is not a function

函数的属性和方法

name 属性

//语句式声明
function f1() {
}
f1.name // "f1"

//表达式声明
var f1 = function add(){
}

f1.name // "add"

递归函数解耦

//递归
function desc( n ){
  console.log( n );
  if(n==0)return;
  arguments.callee( n-1 ); // arguments.callee 存在兼容性问题
}
desc( 10 );

//替换上面的方法,兼容性更好
var desc = function fn( n ){
  console.log( n );
  if( n == 0 ) return;
  fn( n-1 );
}
desc( 5 );

length 属性

函数的长度指的是形参的个数

function fn( a, b, c, d ){
  console.log( fn.length );
}
console.log( fn.length ); // 4
fn(); // 4

toString() 函数的toString方法返回一个字符串,内容是函数的源码。

function add( a, b ){
  /*
  这是一个
  多行注释
  */
  return a+b;
}
console.log( add.toString() );

函数作用域

作用域(scope)指的是变量存在的范围

js两种作用域:

  • 全局作用域(全局变量):

变量在整个程序中一直存在,所有地方都可以读取,关闭当前窗口,才能释放内存。

<script>
var str = '张三';//全局变量
</script>

  • 函数作用域(局部变量):

变量只在函数内部存在

注意:在函数内部使用var关键字声明的变量是局部变量

function fn(){
  var str = '李四';
}
fn();
console.log( str ); //报错 str is not defined

注意:在函数内部省略var关键字,会自动提升为全局变量。

function fn(){
  str = '李四';
}
fn();
console.log( str ); // 李四

<script>
var str = '张三';//全局变量
function fn(){
  var age = 20; //局部变量
}
</script>

变量提升面试题 1

fn(); // undefined 20

var str = '张三'; 
function fn(){
  var age = 20;
  console.log( str, age);
}


变量提升面试题 2

function fn(){
  if ( false ) {
    var x = 5;
  }
  console.log(x); 
}

fn(); // undefined

变量提升面试题 3

//代码从上到下执行
function x(){
  console.log(a);
  a = 10;
}
x();
console.log( a ); // Uncaught ReferenceError: a is not defined

变量提升面试题 4

function x(){
  console.log(a);
  var a = 10;
}
x(); // undefined
console.log( a ); // Uncaught ReferenceError: a is not defined

函数作用域面试题 1

var str = '张三'; 
function fn(){
  var age = 20;
  var str = '李四';
  console.log( str, age);
}

fn(); // 李四 20

console.log( str ); // 张三

函数作用域面试题 2

var str = '张三'; 
function fn(){
  var age = 20;
  str = '李四';
  console.log( str, age);
}

fn(); // 李四 20

console.log( str ); // 李四

函数作用域面试题 3

function one( ){

  var str = '张三';
  function two(){
    var age = 20;
    console.log( str );
  }
  two();

  console.log( age );

}

one(); // 报错 

函数本身的作用域

函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时(调用时)所在的作用域无关

情况 1

var a = 1;
//声明时作用域
var x = function(){
  console.log(a);
}

function f() {
  var a = 2;
  //运行时(调用时)作用域
  x();
}

f();//1

情况 2

var a = 1;
//声明时作用域
var x = function(){
  var a = 5;
  console.log(a);
}

function f() {
  var a = 2;
  //运行时(调用时)作用域
  x();
}

f();//5

情况 3

//声明时作用域
var x = function(){
  console.log(a);
}

function f() {
  var a = 2;
  //运行时(调用时)作用域
  x();
}

f(); // a is not defined

情况 4

//声明时作用域
var x = function(){
  console.log(a);
  a = 10;
  //注意没有使用var关键字 则没有优先提升权限
}

function f() {
  var a = 2;
  //运行时(调用时)作用域
  x();
}

f(); // Uncaught ReferenceError: a is not defined

函数闭包

在函数内部返回函数,返回的函数调用了父函数的局部变量,这样形成了闭包。

  • 闭包可以将函数内部的局部变量暴露给外部,是函数内部和外部的桥梁
  • 闭包会导致函数内部的局部变量无法释放,不被垃圾回收站回收。

闭包的缺点:闭包因为会使局部变量内存无法及时释放,如果使用太多会增加内存开销,带来性能隐患。

function one(){
  var name = '张三';
  function two(){
    console.log( name );
  }
  return two;
}

var fn = one();
fn();

闭包经典面试题 1

function bb(){
  var num = 999;
  return function(){
    num++;
    console.log( num );
  }
}

var fun = bb();// function(){}
fun(); // 1000
fun(); // 1001

参数

函数运行的时候,有时需要提供外部数据,不同的外部数据会得到不同的结果,这种外部数据就叫参数。

function square(x) {
  return x * x;
}

参数可以省略,从后向前省略,形参和实际参数一一对应。

运行时无论提供多少个参数,或者不提供参数都不会报错,省略的参数值是undefined

函数的length只和形式参数个数有关,和实际参数无关

没有办法只省略靠前的参数,而保留靠后的参数,从后向前省略。

形参的作用域是函数作用域,也就是局部变量

//1 使用条件语句
function add( a, b, c){
  if(a==undefined) a =0;
  if(b==undefined) b =0;
  if(c==undefined) c =0;
  return a + b + c;
}

console.log(  add( 1, 2 ) );

//2 使得短路法则
function add( a, b, c){
  a = a || 0;
  b = b || 0;
  c = c || 0;
  return a + b + c;
}

console.log(  add( 1, 2 ) );

//3 默认参数 es6
function add( a = 0, b = 0, c = 0){
  return a + b + c;
}
console.log(  add( 1, 2 ) );

函数参数传递方式
  • 参数为原始类型(数值、字符串、布尔、undefined、null)传递方式是传值传递。
  • 参数为引用类型(对象、数组)传递方式是传址传递。

比较下面两道题,注意区分:

//函数内部没有加var关键字,认为全局变量
var p = 2;
function f() {
  p = 3;
}
f();
console.log( p );//3

//因为p是形参,而它是直接拷贝 2,内部覆盖它的值为3 不会影响到全局变量p
var p = 2;
function f( p ) {
  p = 3;
}
f( p );
console.log( p );//2

数组是传址传递,所以在数组内部会影响到外面传入的数组

var arr = ['苹果','桔子'];
function f( arr ) {
  arr[1] = '香蕉';
}
f( arr );
console.log( arr );//['苹果','香蕉']

解决办法,就是复制一个新数组

//使用es6扩展运算符生成新数组
var arr = ['苹果','桔子'];
function f( [...arr] ) {
  arr[1] = '香蕉';
}
f( arr );
console.log( arr );//['苹果','桔子']

//使用concat生成新数组
var arr = ['苹果','桔子'];
function f( arr ) {
  arr = [].concat( arr );
  arr[1] = '香蕉';
}
f( arr );
console.log( arr );//['苹果','桔子']


对象因为也是引用类型,所有在函数内部修改对象时会改变外面的对象

var obj = {
  name: '张三',
  info: {
    age: 20
  }
};

function f( obj ) {
  obj.info.age = 30;
}
f( obj );
console.log( obj );//年龄会被该成30


同名参数

如果有同名的参数,则取最后出现的那个值。

即使后面的a没有值或被省略,也是以其为准。

function f(a, a) {
  console.log(a);
}

f(1, 2) // 2

arguments 参数集合对象

由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来。

arguments 是一个类数组对像,它包含了数组所有参数。

arguments 对象包含了函数运行时的所有参数(实际参数)

arguments.callee 指向的是函数本身

function max(){
  var args = arguments;
  var arr = [];

  if( args.length > 1 ){
    arr = Array.from( args );
  }else{
    if( typeof args[0] =='string' ){
      arr = args[0].split('');
    }else{
      arr = args[0];
    }
  }

  arr = arr.filter(function(item){
    return isNaN(item) == false;
  })

  var max = arr[0];
  for(var i=0;i<arr.length;i++){
    if ( max < arr[i] ) max = arr[i];
  }
  console.log( max );

}

max('230a1'); 
max(2,'a',30,1);
max([2,'a',13,1]);

经典面试题:多种办法单击获取索引值

<ul>
    <li>首页</li>
    <li>关于我们</li>
    <li>产品中心</li>
</ul>
<script>
var lis = document.querySelectorAll('li');

//1. es6 块级作用域实现
for(let i = 0; i < lis.length; i++ ){
  lis[i].onclick = function(){
    console.log( i );
  }
}

//2.将索引值缓存到对象上,然后再到对象自身去取
for(var i = 0; i < lis.length; i++ ){
  lis[i].index = i;
  lis[i].onclick = function(){
    console.log( this.index );
  }
}

//3. 使用闭包来缓存索引值
for(var i = 0; i < lis.length; i++ ){
  function fn( j ){
    lis[j].onclick = function(){
      console.log( j );
    }
  }
  fn( i );
}

//4. 使用数组的indexOf方法获取索引值
var arr = Array.from( lis );
for(var i = 0; i < lis.length; i++ ){
  lis[i].onclick = function(){
    console.log( arr.indexOf( this ) );
  }
}
</script>

立即调用的函数表达式 IIFE

圆括号()是一种运算符,跟在函数名之后,表示调用该函数。

比如,print()就表示调用print函数。

!function () { /* code */ }();
~function () { /* code */ }();
-function () { /* code */ }();
+function () { /* code */ }();

//官方建议
( function(){ /* code */ } )();
( function(){ /* code */ }() );

示例

+function( a, b ){
  console.log(a+b);
}(2,3);

(function( a, b ){
  console.log(a+b);
})(2,3);

(function( a, b ){
  console.log(a+b);
}(2,3));

立即执行函数用途:

  • 不必为函数命名,避免了污染全局变量
  • IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量

用js写原生插件或者组件,我们习惯用IIFE封装它,避免污染全局变量。

(function(){

  var iAlert = function(){
    console.log('我是警告框插件!');
  }

  //将私有的变量注册为全局变量
  window.iAlert = iAlert;

}());

//调用插件
iAlert();

定义组件时,组件默认参数通常采用覆盖替换法实现

//默认参数
var oldObj = {
  page: 1,
  perpage: 5
}

//用户参数
var newObj = {
  page: 3,
  perpage: 10
}

//合并默认参数和用户参数
var obj = Object.assign( {}, oldObj, newObj);
// var obj = { ...oldObj, ...newObj };
console.log( obj );

js 严格模式

默认是比较宽松的模式

如何设置严格模式?

全局设置

<script>
//设置严格模式
'use strict'

</script>

函数内设置

<script>
//函数内设置严格模式
function fn(){
  'use strict'

}
</script>

严格模式有那些要求(部分)?

  1. 声明变量需要使用关键字varletconst,否则报错
'use strict'
str = '张三';
console.log(str); //报错

  1. 不允许函数的形参重名
'use strict'
function x(p1, p1) {
} //报错

  1. 不允许删除一个不允许删除的属性
'use strict';
delete Object.prototype; //错误

  1. 变量名不能使用 arguments 字符串
'use strict';
var arguments = 10;
console.log( arguments );

  1. 由于一些安全原因,在作用域 eval() 创建的变量不能被调用
'use strict';
eval ('var x = 2');
alert (x); 

  1. 禁止this关键字指向全局对象。
'use strict';
function fn(){
  console.log(this);
}

fn();//本来是指向windows,但在严格模式下不允许,返回undefined

  1. 不允许删除变量或对象。
'use strict';
var x = 3.14;
delete x; 

使用new Function 将字符串当作命令执行。

语法

new Function('参数1','参数2','...','函数内部返回');

最后一个参数始终是函数内部返回代码

var jsonp = 'foo({"id": 42})';

// foo是函数的参数,它是一个函数(回调函数)。
var f = new Function( 'foo', jsonp );

// 相当于定义了如下函数 
// function f(foo) {
//   foo({"id":42}); //回调函数
// }

//调用 
f(function (json) {
  console.log( json.id ); // 42
})

jsonp原理

为了便于客户端使用数据(比如远程数据),逐渐形成了一种非正式传输协议,进行数据传送,这种方式我们叫JSONP跨域,该协议的一个要点就是允许服务端返回一个回调函数,这个回调函数包裹这数据,这样客户端就可以方便的取到服务器返回的数据。

jsonp主要原理是利用js回调函数机制实现一种非正式跨域。

  • 远程服务端返回一个函数,该函数包裹数据
//服务端
//假设以下 http://www.jx-tongan.com/vueapi/hello.js 的内容
fn({
  name:'张三',
  age: 20,
  habby: ['钓鱼','游泳']
});

  • 客户端通过回调函数的形参获取到服务端返回的数据
<script>
//客户端
//服务端的数据在形参obj上 
function city( obj ){
  console.log( obj );
}  
</script>

<script src="js/city.js"></script>

运算符

算术运算符
  • 指数运算符(注意兼容性)
console.log( 2 ** 2 );// 4
console.log( 2 ** 3 );// 8

  • 取余
console.log( 10 % 3); // 1
console.log( 13 % 6); // 1

  • 自增运算符

注意下面代码的区别

//示例 1 
var a = 0;
var b = 0;
a++;
++b;
console.log( a, b ); //1 1

//示例 2
var x = 0;
var y = 0;

console.log( x++, ++y ); //0 1

赋值运算符

最常见的赋值运算符,当然就是等号(=)。

var x = 1;
var x = y;

赋值运算符还可以与其他运算符结合,形成变体

// 等同于 x = x + y
x += y

// 等同于 x = x - y
x -= y

// 等同于 x = x * y
x *= y

// 等同于 x = x / y
x /= y

// 等同于 x = x % y
x %= y

// 等同于 x = x ** y
x **= y

比较运算符

提供了8个比较运算符:

  • < 小于运算符
  • > 大于运算符
  • <= 小于或等于运算符
  • >= 大于或等于运算符
  • == 相等运算符
  • === 严格相等运算符
  • != 不相等运算符
  • !== 严格不相等运算符

字符串按照字典顺序进行比较。

'cat' > 'dog' // false
'cat' > 'catalog' // false
'大' > '小' // false

上面代码中,“大”的 Unicode 码点是22823,“小”是23567,因此返回false。

两个原始类型的值的比较,除了相等运算符()和严格相等运算符(=),其他比较运算符都是先转成数值再比较。

+0 === -0 // true

{} === {} // false
[] === [] // false
(function () {} === function () {}) // false

new Date() > new Date() // false
new Date() < new Date() // false
new Date() === new Date() // false

NaN === NaN  // false

undefined === undefined // true
null === null // true


false == null // false
false == undefined // false

0 == null // false
0 == undefined // false

undefined == null // true

相等运算符隐藏的类型转换,会带来一些违反直觉的结果。

0 == ''             // true
0 == '0'            // true

2 == true           // false
2 == false          // false

false == 'false'    // false
false == '0'        // true

false == undefined  // false
false == null       // false
null == undefined   // true

' \t\r\n ' == 0     // true

上面这些表达式都很容易出错,因此不要使用相等运算符(==),最好只使用严格相等运算符(===)。

布尔运算符
  • !:取反运算符
  • &&:且运算符
  • ||:或运算符
  • ?::三元运算符

取反运算符是一个感叹号,用于将布尔值变为相反值,即true变成falsefalse变成true

用取反来写开关效果

<button id="btn">按钮</button>
<script>
var flag = false;
document.getElementById('btn').onclick = function(){
  flag = !flag;
  console.log(flag);
}
</script>

!! 相当于使用Boolean转换为布尔类型

!!x
// 等同于
Boolean(x)

示例

var str = '111';
console.log( Boolean( str ) );//true
console.log( !!str );//true

且运算符(&&)

它的运算规则是:

  • 如果第一个运算符的布尔值为true,那么返回第二个运算符的值。
  • 如果第一个运算符的布尔值为false,那么返回第一个运算符的值。
true && 'hello'; // hello
false && 'hello'; // false

'张三' && 20; // 20
'0' && 'ab'; // ab
0 && 'ab'; // 0
1>2 && ''; // false
'' && 'ab'; // ''

1 && 0 && 'ab'; // 0
'a' && 'null' && 0; // 0

var age = 20;
if( age > 18 ) console.log('成年!');
console.log( age > 18 && '成年!' );

或运算符(||)

它的运算规则是:

  • 如果第一个运算符的布尔值为true,那么返回第一个运算符的值。
  • 如果第一个运算符的布尔值为false,那么返回第二个运算符的值。
true || 'ab'; // true
false || 'ab'; // ab
'false' || 'ab'; // false
0 || null; // null
'' || 0 || null || 'ab'; // ab
'ab' || null || 0; // ab

且运算:遇真返回第2个值,遇假直接输出假。
或运算:遇假返回第2个值,遇真直接输出真。

//且运算:遇真返回第2个值,遇假直接输出假。
//或运算:遇假返回第2个值,遇真直接输出真。

var str1 = 'he' && '0' && [] || 20 || {};
console.log( str1 );// []

var str2 = 'he' && '0' && '' || 0 || {};
console.log( str2 );// {}

用且或来替代条件语句

if (i) {
  doSomething();
}

// 等价于

i && doSomething();

三元条件运算符

语法

console.log( true ? '真' : '假');

't' ? 'hello' : 'world' // "hello"
0 ? 'hello' : 'world' // "world"

void 运算符

void运算符的作用是执行一个表达式,然后不返回任何值,或者说返回undefined

console.log( 3*5 ); // 15
console.log( void(3*5) ); // undefined

阻止超级链接默认事件

<div style="height:2000px;"></div>

<a href="javascript:void(0)">连接1</a>
<a href="javascript:;">连接2</a>
<a>连接3</a>

逗号运算符

逗号运算符用于对两个表达式求值,并返回后一个表达式的值。

注意要加上括号,不然会报错

var str1 = ( 2+4, 3*5 );
console.log( str ); //15

var str2 = ( a = 2+4, a + 3*5 );
console.log( str );//21

var str3 = ( a = 2+4, 3*5 );
console.log( a, str ); //6 15

错误处理

JavaScript 解析或运行时,一旦发生错误,引擎就会抛出一个错误对象,JavaScript 原生提供Error构造函数,所有抛出的错误都是这个构造函数的实例。

创建错误对象实例

var err = new Error('出错了');
err.message // "出错了"

try catch 错误捕获语句,它将语句在沙箱中运行,在语句中可以抛出错误。避免发送致命错误,导致语句中断无法继续。

//错误捕获
try {
  if(!document.getElementById('box1')){
    throw new Error('错误:不存在的对象!');
  }
  document.getElementById('box1').innerText='错误代码演示';
} catch( e ) {
  //e 是错误对象
  console.dir(e);
} finally {
    console.log('无论对错都会执行');
}

console.log('代码继续执行');

js错误类型

  • SyntaxError: 语法错误
// 1. Syntax Error: 语法错误
// 1.1 变量名不符合规范
var 1       // Uncaught SyntaxError: Unexpected number
var 1a       // Uncaught SyntaxError: Invalid or unexpected token
// 1.2 给关键字赋值
function = 5     // Uncaught SyntaxError: Unexpected token =

  • Uncaught ReferenceError: 引用错误

引用一个不存在的变量时发生的错误
将一个值分配给无法分配的对象,比如对函数的运行结果或者函数赋值。

// 2.1 引用了不存在的变量
a()       // Uncaught ReferenceError: a is not defined
console.log(b)     // Uncaught ReferenceError: b is not defined
// 2.2 给一个无法被赋值的对象赋值
console.log("abc") = 1   // Uncaught ReferenceError: Invalid left-hand side in assignment

  • RangeError: 范围错误
// 3.1 数组长度为负数
[].length = -5      // Uncaught RangeError: Invalid array length
// 3.2 Number对象的方法参数超出范围
var num = new Number(12.34)
console.log(num.toFixed(-1))   // Uncaught RangeError: toFixed() digits argument must be between 0 and 20 at Number.toFixed
// 说明: toFixed方法的作用是将数字四舍五入为指定小数位数的数字,参数是小数点后的位数,范围为0-20.

  • TypeError 类型错误
    变量或参数不是预期类型时发生的错误。比如使用new字符串、布尔值等原始类型和调用对象不存在的方法就会抛出这种错误,因为new命令的参数应该是一个构造函数。
// 4.1 调用不存在的方法
123()        // Uncaught TypeError: 123 is not a function
var o = {}
o.run()        // Uncaught TypeError: o.run is not a function
// 4.2 new关键字后接基本类型
var p = new 456      // Uncaught TypeError: 456 is not a constructor

  • URIError,URL 错误
    主要是相关函数的参数不正确。
decodeURI("%")     // Uncaught URIError: URI malformed at decodeURI

URI相关参数不正确时抛出的错误,主要涉及encodeURI、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()六个函数。

  • EvalError eval() 函数执行错误
    在ES5以下的JavaScript中,当eval()函数没有被正确执行时,会抛出evalError错误。
var myEval = eval;
myEval("alert('call eval')");

编程风格

缩进

缩进建议统一使用空格(1个tab=2个空格,1个tab=4个空格),因为不同操作系统对tab解析不不太一样。

//bad
function fn(){
if(1==1){
console.log('aaa');
}else{
console.log('bbb');
}
}

//good
function fn(){
  if(1==1){
    console.log('aaa');
  }else{
    console.log('bbb');
  }
}

区块

建议总是使用大括号表示区块

function fn()
{

}

//well
function fn() {

}

圆括号
  • 函数的调用
// 表示函数定义时,函数名与左括号之间没有空格。

//bad
function add () {
}
//good
function add() {
}

//表示函数调用时,函数名与左括号之间没有空格。
//bad
add()

//good
add();

// 其他情况时,前面位置的语法元素与左括号之间,都有一个空格。

//bad
foo (bar);
return(a+b);
if(a === 0) {...}
function foo (b) {...}
function(x) {...}

//good
foo(bar);
return (a+b);
if (a === 0) {...}
function foo(b) {...}
function (x) {...}

不使用分号的情况

以下三种情况不需要添加分号

for 和 while 循环
for ( ; ; ) {
} // 没有分号

while (true) {
} // 没有分号

注意,do...while循环是有分号的。

do {
  a--;
} while(a > 0); // 分号不能省略

分支语句:if,switch,try
if (true) {
} // 没有分号

switch () {
} // 没有分号

try {
} catch {
} // 没有分号

函数的声明语句
function f() {
} // 没有分号

注意,函数表达式仍然要使用分号。s

var f = function f() {
};

自动添加分号

虽然大部分语法会自动加上分号,但是也存在一些解析歧义,比如分号不能正确的加入合适的位置,建议手动加上分号。

全局变量

JavaScript 最大的语法缺点,可能就是全局变量对于任何一个代码块,都是可读可写。这对代码的模块化和重复使用,非常不利。

因此,建议避免使用全局变量。
如果不得不使用,可以考虑用大写字母表示变量名

var CPAGE = 5;

相等和严格相等

推荐使用 === 代替 ==

自增和自减运算符
//bad
++x

//good
x += 1;

改用+= 1,代码变得更清晰了。

建议自增(++)和自减(--)运算符尽量使用+=-=代替。

switch…case 结构
function doAction( action ) {
  switch (action) {
    case 'hack':
      return 'hack';
      break;
    case 'slash':
      return 'slash';
      break;
    case 'run':
      return 'run';
      break;
    default:
      throw new Error('Invalid action.');
  }
}

上面的代码建议改写成对象结构。

function doAction(action) {
  var actions = {
    'hack': function () {
      return 'hack';
    },
    'slash': function () {
      return 'slash';
    },
    'run': function () {
      return 'run';
    }
  };

  if (typeof actions[action] !== 'function') {
    throw new Error('Invalid action.');
  }

  return actions['run']();
}

因此,建议switch...case结构可以用对象结构代替。

Math对象

该对象不是构造函数,不能生成实例。

var a = 2.67;

//报错,Math对象不是构造函数
var str = new Math();
str.round( a );

// 应该这样写
var str = Math.round( a );//3

静态属性
  • Math.PI:圆周率
  • Math.SQRT2:2 的平方根

这些属性都是只读的,不能修改。

静态方法
  • Math.abs():绝对值
console.log( Math.abs(-1) );//1
console.log( Math.abs(1) );//1

  • Math.ceil():上舍入
console.log( Math.ceil(3.00001) ); //4
console.log( Math.ceil(3.99999) ); //4
console.log( Math.ceil(3.33333) ); //4

  • Math.floor():下舍入
console.log( Math.floor(3.99999) ); //3
console.log( Math.floor(3.00001) ); //3
console.log( Math.floor(3.33333) ); //3

  • Math.round():四舍五入
console.log( Math.round(3.99999) ); //4
console.log( Math.round(3.00001) ); //3
console.log( Math.round(3.33333) ); //3

  • Math.max():最大值
console.log( Math.max(2,1,5,6,7) );// 7
console.log( Math.max.apply( null, [2,1,5,6,7] ) ); // 7

  • Math.min():最小值
console.log( Math.min(2,1,5,6,7) );// 1
console.log( Math.min.apply( null, [2,1,5,6,7] ) ); // 1

  • Math.pow():指数运算

语法:

Math.pow(底数,指数);

console.log( 2 ** 3 ); //8
console.log( Math.pow(2,3 ) );//8

  • Math.sqrt():平方根
// 2的平方根
console.log( Math.SQRT2 );//1.4142135623730951
console.log( Math.sqrt(2) );//1.4142135623730951
console.log( Math.sqrt(4) ); //2

  • Math.random():随机数

返回0~1之间,可能等于0,但是一定小于1。

console.log( Math.random() );

//随机 0 - 10 之间的整数
console.log( Math.round( Math.random() * 10 ) );

//随机 5 - 15 之间的整数
console.log( 5 + Math.round( Math.random() * 10 ) );

//随机取出数组成员(数组可增可减)
var arr = ['苹果','桔子','菠萝'];
var index =  Math.floor( Math.random() * arr.length );//0 - 2
console.log( arr[index] );


//随机打乱一个数组
var arr = [1,2,3,4,5,6,7,8,9];
arr.sort(function(){
  return 0.5 - Math.random();
})
console.log( arr );

setTimeout 单次定时执行函数

语法:

  • fn:函数
  • str:字符串
  • time:时间毫秒(1000毫秒=1秒)
setTimeout( fn|str,time);

示例

//写法一
setTimeout(function(){
  console.log('hello');
},1000);

//写法二
function echo(){
  console.log('hello');
}
setTimeout( echo, 1000);
setTimeout('echo()',2000);

  • 有时候可以用setTimeout模拟异步操作
//打开文件
console.log('正在打开文件...');
setTimeout(function(){
  console.log('文件已打开!');
},3000);
console.log('hello');

// 执行先后顺序如下:
//1.正在打开文件...
//2.hello
//3.文件已打开!

  • 延时操作
setTimeout(function(){
  console.log('暂停3秒');
},3000);

setTimeout对象

setTimeout(function(){
  console.log('ok');
},0);

console.log('hello');

//输出顺序是 hello > ok

经典面试题1

for(var i=0;i<10;i++){

  setTimeout(function(){
    console.log(i);
  },0)

}

//输出10次 10

经典面试题2

for(let i=0;i<10;i++){
  console.log('a:',i);
  setTimeout(function(){
    console.log('b:',i);
  },0)

}
//输出 a: 0 ~ 9
//输出 b: 0 ~ 9

  1. 如果需要手动写动画,你认为最小时间间隔是多久,为什么?

答:多数显示器默认频率是60Hz,即1秒刷新60次,所以理论上最小间隔为1/60*1000ms16.7ms

clearTimeout 清除单次操作定时器
<button id="btn">清除定时</button>
<script>
var time = setTimeout(function(){
  console.log('hello');
},5000);

document.getElementById('btn').onclick = function(){
  clearTimeout( time );
  alert('清除完毕!');
}
</script>

setInterval 多次定时执行函数

语法:

  • fn:函数
  • str:字符串
  • time:时间毫秒(1000毫秒=1秒)
setInterval( fn|str,time);

示例

setInterval(function(){
  console.log('hello');
},1000)

clearInterval 清除多次定时执行函数

简单的随机点名效果

  <h1 id="user"></h1>
  <button id="addbtn">添加</button>
  <button id="btn">启动</button>

<script>
var list = [];
if( localStorage.getItem('list') ){
  list = localStorage.getItem('list').split(',');
}

function play(){

  var num = 3;
  var user = document.getElementById('user');
  if(list.length == 1){
    user.innerHTML = list[0];
    return;
  }
  var int = setInterval(function(){

    user.innerText = num;
    num -= 1;
    if(num < 0) {
      clearInterval( int );
      var index = Math.floor(Math.random() * list.length);
      user.innerText = list[index];
      list.splice(index,1);
      console.log(list);
    }
    
  },1000);

}

document.getElementById('addbtn').onclick = function(){
  var str = prompt('请输入姓名!');
  list.push( str );

  //本地存储
  localStorage.setItem('list', list.toString() );
}

document.getElementById('btn').onclick = function(){

  //启动随机点名
  play();

}


</script>

Date对象

时间起点

它以19701100:00:00作为时间的零点,可以表示的时间范围是前后各1亿天(单位为毫秒)。

时间最小单位

毫秒,换算 1000毫秒 = 1

普通函数的用法
console.log( Date() );
//Tue Jul 30 2019 14:17:35 GMT+0800 (中国标准时间)

console.log( Date(2000, 1, 1) );
//Tue Jul 30 2019 14:17:35 GMT+0800 (中国标准时间)

上面代码说明,无论有没有参数,直接调用Date总是返回当前时间。

构造函数的用法
var d1 = new Date();
//d1 是构造函数Date的实例

var d2 = new Date();

Date不加参数,实例代表的就是当前时间。

//不写参数默认是当前时间
var d = new Date();
console.log(d);
//Tue Jul 30 2019 14:22:45 GMT+0800 (中国标准时间)

var d = new Date(2000,10,20,15,30,56);
console.log(d);
//Mon Nov 20 2000 15:30:56 GMT+0800 (中国标准时间)


时间戳

至时间零点(1970-1-1 0:0:0)到当前时间所经历的毫秒数

var d = new Date(0);
console.log(d);
//Thu Jan 01 1970 08:00:00 GMT+0800 (中国标准时间) 
//因为我们是东八区,比如GMT快8小时

只要是能被Date.parse()方法解析的字符串,都可以当作参数。

new Date('2013-2-15')
new Date('2013/2/15')
new Date('02/15/2013')
new Date('2013-FEB-15')
new Date('FEB, 15, 2013')
new Date('FEB 15, 2013')
new Date('Feberuary, 15, 2013')
new Date('Feberuary 15, 2013')
new Date('15 Feb 2013')
new Date('15, Feberuary, 2013')
// Fri Feb 15 2013 00:00:00 GMT+0800 (CST)

getYear 获取总年数
var d = new Date();
//1900 ~ 至今经历的总年数
console.log( d.getYear() );//119

getFullYear 获取四位年份
var d = new Date();
console.log( d.getFullYear() ); // 2019

getMonth 获取月份

注意:月份是从0开始计算,0表示一月

范围:0 ~ 11

var d = new Date();
console.log( d.getMonth() + 1 ); // 7

getDate 获取月份

范围:1 ~ 31

var d = new Date();
console.log( d.getDate()  ); // 30

getDay 获取星期

注意:0 表示星期日,其余依次类推
范围:0 ~ 6

var d = new Date();
console.log( d.getDay()  ); // 2

getHours 获取小时

范围:0 ~ 23

var d = new Date();
console.log( d.getHours() );//14

getMinutes 获取分钟

范围:0 ~ 59

var d = new Date();
console.log( d.getMinutes()  ); // 50

getSeconds 获取秒钟

范围:0 ~ 59

var d = new Date();
console.log( d.getSeconds()  ); //29

getMilliseconds 获取毫秒

范围:0 ~ 999

var d = new Date();
console.log( d.getMilliseconds()  ); //555

getTime 获取指定日期的毫秒

var d1 = new Date();
var times = d1.getTime();//获取1970到当前所经历的毫秒数

//以上代码也可以改下如下
var times = new Date().getTime();

//获取指定日期的毫秒数
var times = new Date('2019-7-31 10:12').getTime();


正则RegExp对象

正则表达式表达文本模式的方法,常常用来按照“给定模式”匹配文本。

什么是正则对象?

正则对象通常使用斜杠包裹起来的一段字符

正则对象定义

//构造函数定义
var reg = new RegExp('hello');

//缩写定义
var reg = /hello/;

什么是正则对象的修饰符?
  • i:加i,表示不区分大小写
  • g: 加g,表示全局匹配
  • m:加m,表示多行匹配
//1. 表示不区分字母a大小写
var r1 = /a/i; 
var r1 = new RegExp('a','i');

//2. 表示匹配字母a
var r2 = /a/g; 
var r2 = new RegExp('a','g');

//3. 表示全局匹配字母a,字母a不区分大小。
var r3 = /a/gi; 
var r3 = new RegExp('a','gi');


正则对象实例方法
  • test

用于正则模式去匹配给定的文本,返回布尔值。

语法:

返回布尔类型

正则对象.test('搜索的文本');

示例

var str = 'hello';
var reg1 = /e/;
var reg2 = new RegExp('e');

console.log( reg1.test( str ) ); //true
console.log( reg2.test( str ) ); //true
console.log( /E/i.test( 'hello' ) );//true
console.log( new RegExp('E','i').test( 'hello' ) );//true

  • exec

正则实例对象的exec方法,用来返回匹配结果。如果发现匹配,就返回一个数组,成员是匹配成功的子字符串,否则返回null

当有重复匹配,返回最初匹配的索引值

console.log( /e/.exec('hello') ); 
// ["e", index: 1, input: "hello", groups: undefined]

console.log( /l/.exec('hello') ); 

// ["l", index: 2, input: "hello", groups: undefined]

console.log( /w/.exec('hello') ); 
// null

//组匹配
console.log( /_(x)/.exec('_x_x') );
//["_x", "x", index: 0, input: "_x_x", groups: undefined]

var reg = /a/g;
var result = reg.exec('abc_abc_abc');
console.log( reg.lastIndex );//1

var result = reg.exec('abc_abc_abc');
console.log( reg.lastIndex );//5

var result = reg.exec('abc_abc_abc');
console.log( reg.lastIndex );//9


正则对象规则
字面量字符

没有代表意义的字符,也就是普通字符,我们叫字面量字符

/dog/.test('old dog') // true
/张三/.test('名称') // false
/111/.test('名称') // false

元字符

有代表意义的字符,特殊字符

  • 点字符(.):表示任意字符,只匹配一个字符
console.log( /./.test('dsfdsf232131') ); //true
console.log( /./.test('一二三四') ); //true
console.log( /./.test('1122') ); //true
console.log( /./.test('   ') ); //true
console.log( /./.test('') ); //false

如果要匹配的是点?

console.log( /\./.test('jpg') );//false

console.log( /\./.test('.jpg') );//true

注意:如果想要元字符表达字面量字符,可以使用转义符 \

console.log( /:\/\//.test('http://') ); //true

  • 位置字符(^$):
  1. ^ 以什么开头
  2. $以什么结尾
console.log( /^\d/.test('2abc') ); //以数字开头

console.log( /\d$/.test('abc2') ); //以数字结尾

console.log( /^\d$/.test('abc2') ); //以数字开头并且以数字结尾,\d只能匹配单个字符


  • 数字符 \d: 匹配数字,只匹配一个字符
console.log( /\d/.test('d') ); //false

console.log( /\d/.test('3423') ); //true

console.log( /\d/.test('abb1a中') ); //true

console.log( /^\d/.test('1abba中') ); //true

console.log( /^\d$/.test('1abba中1') ); //false
  
console.log( /^\d$/.test('1a') ); //false

console.log( /^\d$/.test('1') ); //true

console.log( /^\d$/.test('123') ); //false,只匹配一个

  • 数字符 \D: 匹配非数字,只匹配一个字符
console.log( /^\D$/.test('张') ); //true
console.log( /^\D$/.test('1') ); //false

  • 字母数字下划线符 \w:匹配大小字母(a-z A-Z)、数字(0-9)、下划线_
console.log( /^\w$/.test('张') ); //false

console.log( /^\w$/.test('1') ); //true

console.log( /^\w$/.test('a') ); //true

console.log( /^\w$/.test('A') ); //true

console.log( /^\w$/.test('_') ); //true

console.log( /^\w$/.test('-') ); //false

console.log( /^\w$/.test('a1') ); //false

  • 非字母数字下划线符 \W: 不匹配非数字,只匹配一个字符
console.log( /^\W$/.test('张') ); //true
console.log( /^\W$/.test('@') ); //true
console.log( /^\W$/.test('%') ); //true

  • 选择符(|):类似于或的意思
console.log( /^(\d|a)/.test('f') ); //false

console.log( /^(\d|a)/.test('7') ); //true

console.log( /^(\d|a)/.test('7adfg') ); //true

console.log( /^(\d|a)/.test('a1212') ); //true

  • 特殊字符
  1. \n:匹配换行
console.log( /\n/.test(`111 333 222 444`) );//false
console.log( /\n/.test(`111
333`) );//true

  1. \s:匹配空格、换行\n、回车\r、制表符\t
  console.log( /\s/.test(`111 333 222 444`) );//true
  console.log( /\s/.test(`111
333`) );//true

如果我只想匹配纯空格呢?

console.log( / /.test(`111 333 222 444`) );//true

  1. \S: 匹配非空格、换行\n、回车\r、制表符\t
  2. \uhhhh:匹配一个以四位十六进制数 \u0000-\uFFFF 表示的 Unicode 字符

注意:[\u4e00-\u9fa5]:表示中文字符串集范围,表示单个汉字

console.log( /[\u4e00-\u9fa5]/.test(`2`) );//false
console.log( /[\u4e00-\u9fa5]/.test(`a`) );//false
console.log( /[\u4e00-\u9fa5]/.test(`$`) );//false
console.log( /[\u4e00-\u9fa5]/.test(`张`) );//true
console.log( /[\u4e00-\u9fa5]/.test(`张1`) );//true
console.log( /^[\u4e00-\u9fa5]$/.test(`张1`) );//false
console.log( /^[\u4e00-\u9fa5]$/.test(`张`) );//true

  • 白名单符[]:可选范围
//允许输入数字或者下划线
console.log( /^[0-9_]$/.test(`2`) );//true
console.log( /^[0-9_]$/.test(`a`) );//false
console.log( /^[0-9_]$/.test(`_`) );//true

  • 黑名单符[^]:除可选范围以为
//不允许输入数字或者下划线
console.log( /^[^0-9_]$/.test(`2`) );//false
console.log( /^[^0-9_]$/.test(`a`) );//true
console.log( /^[^0-9_]$/.test(`_`) );//false

  • 连字符 -:某些情况下,对于连续序列的字符,连字符(-)用来提供简写形式
console.log( /^[1-5]$/.test(`2`) );//
console.log( /^[c-f]$/.test(`C`) );//false
console.log( /^[c-f]$/.test(`d`) );//true
console.log( /^[c-f]$/.test(`j`) );//false
console.log( /^[三-五]$/.test(`四`) );//false
console.log( /^[三-五]$/.test(`三`) );//true
console.log( /^[三-五]$/.test(`五`) );//true

  • 分组():将一组字符串分组匹配
console.log(/(abc){2}/.test('abcabc123')); //true
console.log(/(abc){2}/.test('abc123')); //false

  • 匹配边界\b
var str = 'a|b|c';
var str = '1|2|3|4';

console.log( /^(.+\b\|)+[^\|]$/.test( str ) ); 

  • 匹配非边界\B
量词
  • *:代表 0个、1个、n个情况,可有可无或者很多个。
console.log( /^\d*$/.test('') );  //true 表示0个
console.log( /^\d*$/.test('9') ); //true 表示1个
console.log( /^\d*$/.test('999') ); //true 表示n个
console.log( /^\d*$/.test(' ') );//false 空格不是数字

  • +:代表 1个、n个情况,至少1个。
console.log( /^\d+$/.test('') );  //false 至少1个
console.log( /^\d*$/.test('9') ); //true 表示1个
console.log( /^\d*$/.test('999') ); //true 表示n个
console.log( /^\d*$/.test(' ') );//false 空格不是数字

  • ?:代表 0个、1个情况,要么没有要么有一个。
console.log( /^@?$/.test('') );  //true
console.log( /^@?$/.test('@') );  //true
console.log( /^@?$/.test('@@') );  //false

  • {n}:代表允许字符的固定位数
//必须输入6位数字,这很符合邮政编码规则
console.log( /^\d{6}$/.test('12345') );  //false
console.log( /^\d{6}$/.test('123456') );  //true

  • {n,}:代表至少允许n个字符
console.log( /^\d{6,}$/.test('12345') );//false
console.log( /^\d{6,}$/.test('123456') );//true 
console.log( /^\d{6,}$/.test('123456789') );//true

  • {n,m}:代表至少允许n个字符,最多m个字符
console.log( /^\d{6,10}$/.test('12345') );//false
console.log( /^\d{6,10}$/.test('123456') );//true 
console.log( /^\d{6,10}$/.test('123456789') );//true
console.log( /^\d{6,10}$/.test('12345678921311') );//false

贪婪和非贪婪模式
  • 贪婪模式,默认贪婪模式
var s = 'aaa';
//贪婪模式
console.log( s.match(/a+/) );//aaa

//非贪婪模式
console.log( s.match(/a+?/) );

//查找 <字母>任意字符</字母>
var str = '<div><h2>标题2</h2></div>';

//贪婪模式
console.log( str.match(/^<(.+)>(.*)<\/(.+)>$/) );
// <div><h2>标题2</h2></div>

//非贪婪模式
console.log( str.match(/<(\w+)[^>]+>.*?<\/(\w+)>/) );
// <div><h2>标题2</h2>

  • 非贪婪模式

如果想将贪婪模式改为非贪婪模式,可以在量词符后面加一个问号。

var s = 'aaa';
s.match(/a+?/) // ["a"]

m 修饰符
/world$/.test('hello world\n') // false
/world$/m.test('hello world\n') // true

上面的代码中,字符串结尾处有一个换行符。如果不加m修饰符,匹配不成功,因为字符串的结尾不是world;加上以后,$可以匹配行尾。

/^b/.test('a\nb') // false
/^b/m.test('a\nb') // true

字符串对象实例使用正则的方法
match

返回一个数组,成员是所有匹配的子字符串

replace

按照给定的正则表达式进行替换,返回替换后的字符串。

语法:

字符串.replace(/正则对象/修饰符,用来替换的字符);

示例

`1aa23A45`.replace(/a/,'*');//1*a23A45
`1aa23A45`.replace(/a/g,'*');//1**23A45
`1aa23A45`.replace(/a/gi,'*');//1**23*45

replace方法的第二个参数可以使用美元符号$,用来指代所替换的内容。

var str1 = 'hello world'.replace(/(\w+)\s(\w+)/, '$2 $1');
console.log( str1 );// world hello

var str2 = '3 and 5'.replace(/(\d+)\sand\s(\d+)/,'$2 and $1');
console.log( str2 );// 5 and 3

  • $&: 匹配的子字符串
  • $: 匹配结果前面的文本。
  • $': 匹配结果后面的文本。
  • $n:匹配成功的第n组内容,n是从1开始的自然数。
  • $$:指代美元符号$
'abc'.replace('b', '[$`-$&-$\']');//a[a-b-c]c

'abc'.replace('b', '[$`-$&-$\']');

注意:用来替换的字符也可以是一个函数

var str = '3 and 5'.replace(/[0-9]+/g, function (match) {
  return 2 * match;
})

console.log( str ); // 6 and 10

经典面试题

替换为驼峰法命名

var str1 = 'first-name'; //firstName
var str2 = 'border-top-left-radius';//borderTopLeftRadius
var str3 = 'FiRSt-Name';//firstName

var result = str3.toLowerCase().replace(/-[a-z]/g, function( match ){
  return match.slice(1).toUpperCase();
});

console.log( result );

正则如何使用变量替换
//定义敏感字
var badwords = ['shit','fuck','tmd'];

var str = '今天shit的天tmd气fuck不错';

//批量替换敏感字
for(var i=0;i<badwords.length;i++){
  var reg = new RegExp( badwords[i] , 'gi' );
  str = str.replace( reg, '*' );
}

document.write( str );

正则替换html标签

清理html标签

//html文本
var str = `<div style="color:red;">
  <span class="red">红色</span>
  <h1>标题1</h1>
  <span>文本</   span>
  <p>段落</p>
</div>`;

//1. 清理html标签
str = str.replace(/<.*?>/g,'');
console.log( str );

2. 清理span标签
str = str.replace(/<\/?(\s*)span(.*?)>/gi,'');
console.log(str);

//3. 清理自定义标签
var lists = ['span','p','h1'];
for( var i=0;i<lists.length;i++){
  var reg = new RegExp('<\/?(\\s*)'+lists[i]+'(.*?)>','gi');
  str = str.replace( reg, '' );
}
console.log( str );

//4. 给字符串对象扩展过滤标签的方法
String.prototype.reTags = function(){
  var str = this;
  var args = arguments;
  for(var key in args){
    var reg = new RegExp(`<\/?(\\s*)${args[key]}(.*?)>`,'gi');
    str = str.replace( reg,'');
  }
  return str;
}

var result = str.reTags('span','p','h1');
console.log( result );

先行断言

x(?=y)称为先行断言,x只有在y前面才匹配,y不会被计入返回结果。

示例

//60只有在%前面才匹配,%不会被计入返回结果。
console.log( '60%'.match(/\d+(?=%)/) );//60
console.log( '%60'.match(/\d+(?=%)/) );//null
console.log( '60'.match(/\d+(?=%)/) );//null

先行否定断言
//先行断言

//60只有在%前面才匹配,%不会被计入返回结果。
console.log( '60%'.match(/\d+(?=%)/) );//60
console.log( '%60'.match(/\d+(?=%)/) );//null
console.log( '60'.match(/\d+(?=%)/) );//null

console.log( '80%12%34903%33'.match(/\d+(?=%)/g) );
// ['80','12','34903']

console.log('a.jpg,b.gif,c.png'.match(/\w+(?=,)|\w+(?=$)/g));
//['a.jpg','b.gif','c.png']

var str = 'a.jpg,b.gif,c.png';
console.log( str.match(/(\w+)(?=(,|$))/g) );
//['a.jpg','b.gif','c.png']

先行否定断言

x(?!y)x只有不在y前面才匹配,y不会被计入返回结果。

//先行否定断言

//60只有不在%前面才匹配,%不会被计入返回结果。
console.log( '60%'.match(/\d+(?!%)/) );//6
console.log( '%60'.match(/\d+(?!%)/) );//60

JSON对象

JSON对象本质其实就是一段字符串,只是这个字符串它有一定规则。

目的是取代繁琐笨重的 XML 格式

json文本键名是用双引号,键值如果是字符串,需要双引号,数字可以不需要

数组或对象最后一个成员的后面,不能加逗号

什么xml文本?

<?xml version="1.0" encoding="utf-8"?>
<students>
	<student>
		<sn>201709000123</sn>
		<name>张嘉佳</name>
		<sex></sex> 
		<nation>汉族</nation>
		<address>湖北武汉</address>
		<profession>英语</profession>
	</student>
	<student>
		<sn>201709000124</sn>
		<name>李文斌</name>
		<sex></sex> 
		<nation>傣族</nation>
		<address>云南昆明</address>
		<profession>建筑</profession>
	</student>
</students>

什么json文本?

[
  {
    "sn": "201709000123",
    "name": "张嘉佳",
    "sex": "女",
    "nation":"汉族",
    "address":"湖北武汉",
    "profession":"语言"
  },
  {
    "sn": "201709000123",
    "name": "张嘉佳",
    "sex": "女",
    "nation":"汉族",
    "address":"湖北武汉",
    "profession":"语言"
  }
]

jsonxml比较:

  • 可读性:xml
  • 生成数据方面:json
  • 传输速度方面:json
  • xml诞生很早,可以支持一些很老的设备

json文本中复合类型中不能有函数

以下写法错误

{
  "getname": function(){

  }
}

以下写法正确

{
  "name": "张三",
  "info": [
    {
      "id": 1,
      "title": "标题1",
      "check": "true"
    }
  ]
}

以下都是合法的 JSON。

["one", "two", "three"]

{ "one": 1, "two": 2, "three": 3 }

{"names": ["张三", "李四"] }

[ { "name": "张三"}, {"name": "李四"} ]

以下都是不合法的 JSON。

{ name: "张三", 'age': 32 }  // 属性名必须使用双引号

[32, 64, 128, 0xFFF] // 不能使用十六进制值

{ "name": "张三", "age": undefined } // 不能使用 undefined

{ "name": "张三",
  "birthday": new Date('Fri, 26 Aug 2011 07:13:10 GMT'),
  "getName": function () {
      return this.name;
  }
} // 属性值不能使用函数和日期对象

注意,null、空数组和空对象都是合法的 JSON 值。

JSON对象全局方法
  • JSON.stringify()

返回经过转换过后的字符串

语法:

JSON.stringify(需要转换的对象或数组,用于过滤指定属性,可读性);

示例

JSON.stringify('abc') // ""abc""
JSON.stringify(1) // "1"
JSON.stringify(false) // "false"
JSON.stringify([]) // "[]"
JSON.stringify({}) // "{}"

JSON.stringify([1, "false", false])
// '[1,"false",false]'

JSON.stringify({ name: "张三" })
// '{"name":"张三"}'

第二参数示例: 过滤指定的属性

var obj = {
  'name': '张三',
  'age': '20',
  'sex': '男'
};
var selectedProperties = ['name', 'sex'];
var str = JSON.stringify(obj, selectedProperties);
console.log( str );//{"name":"张三","sex":"男"}

第三参数示例:

var obj = {
  'name': '张三',
  'age': '20',
  'sex': '男',
  info: {
    'a': 20
  }
};
var str = JSON.stringify(obj, null, 2);
//可读性就比较好
// {
//   "name": "张三",
//   "age": "20",
//   "sex": "男",
//   "info": {
//     "a": 20
//   }
// }

JSON.parse()

JSON.parse方法用于将 JSON 字符串转换成对应的值。

JSON.parse('{}') // {}
JSON.parse('true') // true
JSON.parse('"foo"') // "foo"
JSON.parse('[1, 5, "false"]') // [1, 5, "false"]
JSON.parse('null') // null

var o = JSON.parse('{"name": "张三"}');
o.name // 张三

DOM模型

DOM:文档对象模型( Document Object Model ),它的作用是将网页转为一个 js 对象

节点 Node

DOM最小组成单位叫做节点

节点的类型有七种:

  • Document:整个文档节点(包含整个节点树)
  • DocumentType:文档声明类型 <!DOCTYPE html>
  • Element:元素节点,网页的各种HTML标签(<body>divimg等等)
  • Attribute:网页元素的属性(比如class="right"
  • Text:标签之间或标签包含的文本
  • Comment:注释
  • DocumentFragment:文档的片段
根节点

document 根节点,文档的第一层只有一个节点

节点的关系

除了根节点,其他节点都有三种层级关系。

  • 父节点关系
<!-- button的直接父节点是div -->
<div>
  <button>按钮</button>
</div>

  • 子节点关系
<!-- div的直接子节点是button -->
<div>
  <button>按钮</button>
</div>

  • 同级节点关系
<!-- button的同级节点是h2 -->
<div>
  <button>按钮</button>
  <h2>标题2</h2>
</div>

Node 接口的属性

所有 DOM 节点都继承了 Node 接口

  • Node.nodeType:

返回一个整数值,表示节点的类型,有以下类型:

  1. document:9 表示 文档
  2. element: 1 表示 元素
  3. attr: 2 表示 属性
  4. text: 3 表示 文本
  5. DocumentFragment: 11 表示 文档片段
  6. DocumentType: 10 表示 文档类型
  7. Comment: 8 表示 注释
<div id="box">你好</div>
<script>
  var box = document.getElementById('box');
  console.log( box.nodeType ); //1 元素
  console.log( box.childNodes[0].nodeType );//3 文本
</script>

  • Node.nodeName:
<div id="box">你好</div>
<script>
  var box = document.getElementById('box');
  console.log( box.nodeName ); //DIV 默认是大写
  console.log( box.childNodes[0].nodeName );//#text
</script>

  • Node.nodeValue

nodeValue属性返回一个字符串,表示当前节点本身的文本值

注意:该属性主要用于文本节点

var box = document.getElementById('box');
console.log( box.nodeValue );//null
console.log( box.childNodes[0].nodeValue );//您好

  • Node.textContent:

textContent属性返回当前节点和它的所有后代节点的文本内容。

自动忽略当前节点内部的HTML标签,提取文本内容,它和innerText功能一样

该属性可读可写

<div id="box">
  您好
  <p>
    张三
  </p>
</div>

<script>
  var box = document.getElementById('box');
  console.log( box.textContent );// 您好 张三
</script>

  • Node.baseURI

返回一个字符串,表示当前网页的绝对路径

会受<base href="#"> 基础地址影响,而location.href不会。

console.log( document.baseURI );
//file:///F:/studyspace/web1905/javascript/dom.html

console.log( window.location.href );
//file:///F:/studyspace/web1905/javascript/dom.html

  • Node.ownerDocument

返回当前节点所在的顶层文档对象,即document对象。

<div id="box">

</div>

<script>
  var box = document.getElementById('box');
  console.log( box.ownerDocument == document );//true
</script>

节点关系查找
父节点关系
  • Node.childNodes:

    父节点下所有的子节点,注意包含文本、注释、元素节点

  • Node.children:

    父节点下所有的子元素节点,只是元素节点

  <ul id="box">
  <li>一分</li>
  <li>二分</li>
  <li>三分</li>
  <li>四分</li>
  <li>五分</li>
</ul>

<script>
var box = document.getElementById('box');
var lis1 = box.childNodes;
var lis2 = box.children;

console.log( lis1, lis2);
</script>

子节点关系
  • parentNode

    当前节点的直接的父节点

  • parentElement

    当前节点的直接的父元素节点

<div>
  <span>
    <button id="btn">按钮</button>
  </span>
</div>

<script>
document.getElementById('btn').onclick = function(){
  var THIS = this;
  while( THIS.parentElement ){
    THIS = THIS.parentElement;
    if(THIS.nodeName == 'BODY'){
      THIS.style.backgroundColor='red';
    }
  }
}
</script>

同级节点关系
  • Node.nextSibling

    下一个节点,注意包含文本、注释、元素节点

  • Node.nextElementSibling:

    下一个元素节点,只是元素节点。

<button onclick="setBox(this)">按钮</button>
<div></div>

<script>
//单击按钮,给div设置宽200 高200 背景颜色红色
function setBox( btn ){
  btn.nextElementSibling.style.cssText = `width:200px;height:200px;background-color:red`;
}
</script>

  • Node.previousSibling

    上一个节点,注意包含文本、注释、元素节点

  • Node.previousElementSibling

    上一个元素节点,只是元素节点。

    综合的例子

    <ul>
    <li>一分</li>
    <li>二分</li>
    <li>三分</li>
    <li>四分</li>
    <li>五分</li>
    </ul>
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值