es6,js总结

  • Post author:
  • Post category:其他


文章目录

大端 BE

小端 LE



html css



js

https://github.com/yygmind/blog



单线程

在JS中,所谓的异步任务,有三种:

第一,

鼠标键盘事件触发

,例如onclick、onkeydown等等

第二,

网络事件触发

,例如onload、onerror等等

第三,

定时器

,例如setTimeout、setInterval

setTimeout(“console.log(2), 0);
console.log(1);

反复执行这段代码,结果都是先打印1再打印2

因为,setTimeout是个异步任务,

第二个参数真正的含义是,在0毫秒之后,将代码插入任务队列,而不是在0毫秒之后执行。

当插入任务队列后,主线程会继续执行后续的代码,也就是打印结果1,如果此时当前的同步代码已经执行完毕,则主线程立刻会从任务队列中取出最新任务执行。再打印结果2



嵌入js脚本

<body>
  <input type="button" onclick="javascript:document.write('按钮被谁点了一下')" value="点我一下">
</body>

也可以直接在地址栏输入

 javascript:alert(1)



输出

window.alert() // 写入警告框
document.write() // 写入HTML文档中
innerHTML() // 写入HTML元素
console.log() // 控制台
<script>
document.getElementById("demo").innerHTML = "段落已修改";
</script>



换行

var x = "Hello \
World!";



严格模式

  • 不允许使用未声明的变量

  • 不允许删除变量,对象,删除函数 delete x

  • 不允许变量重名

  • 不允许使用

    八进制,转义字符

  • 不允许对只读属性赋值

    "use strict";
    var obj = {};
    Object.defineProperty(obj, "x", {value:0, writable:false});
    obj.x = 3.14
    
  • 变量名不能使用==“eval” “arguments”字符串==

  • 不能用with

    比如,目前现在有一个这样的对象:
    var obj = {
    	a: 1,
    	b: 2,
    	c: 3
    };
    如果想要改变 obj 中每一项的值,一般写法可能会是这样:
    
    // 重复写了3次的“obj”
    obj.a = 2;
    obj.b = 3;
    obj.c = 4;
    
    而用了 with 的写法,会有一个简单的快捷方式
    
    with (obj) {
    	a = 3;
    	b = 4;
    	c = 5;
    }
    

    with弊端,导致数据泄漏

    function foo(obj) {
    	with (obj) {
    		a = 2;
    	}
    }
    
    var o1 = {
    	a: 3
    };
    
    var o2 = {
    	b: 3
    }
    
    foo(o1);
    console.log(o1.a);	//2
    
    foo(o2);
    console.log(o2.a);	//underfined
    console.log(a);		//2,a被泄漏到全局作用域上
    

    参考:https://blog.csdn.net/zwkkkk1/article/details/79725934

不能删除变量

delete prop

,会报错,只能删除属性

delete global[prop]


eval

不会在它的外层作用域引入变量


eval



arguments

不能被重新赋值


arguments

不会自动反映函数参数的变化

不能使用

arguments.callee

以及

arguments.caller

禁止this指向全局对象(顶层的

this

指向

undefined

,应该在顶层代码使用

this

不能使用

fn.caller



fn.arguments

获取函数调用的堆栈

增加了保留字(比如

protected



static



interface



变量



变量名不能是数字

var x = "Porsche" + 911 + 7;
Porsche9117

var x = 911 + 7 + "Porsche";
918Porsche



变量是window的属性

var person = new Object()
person.name = "z"
person.age = 1
console.log(person)

function add() {
    console.log(this) // Window
    console.log(this.person.name, this.person.age)// z 1
}
add()



变量提升

var a = 1
var b = "test"
console.log("a = ", a , b) // a = 1 test

// undifined,undifined 声明了但是没有定义
console.log(x, y) 
var x, y = 5;
// undifined 5  声明了x但是没有定义x
console.log(x, y)



变量提升的作用域

  • 声明提前仅能将声明提前到所在作用域的顶部

  • 不能将变量提升至作用域之外,

      function fn(){
           console.log(a); //undefined
           var a=100;
           console.log(a); //100
      };
      fn();
      console.log(a);//   报错未定义!
    



数据类型



typeof

var a = 3
console.log(typeof(a) === "number")

// 数组用
Arrays.isArray()

string	typeof "小白"	"string"
number	typeof 18	"number"
boolean	typeof true	"boolean"
undefined	typeof undefined	"undefined"
null	typeof null	"object"



== 和 ===

== 比较



,===比较

值和类型

var s = "s"
var s2 = new String("s")
s === s2 // false

console.log(123 == 123n) // 要注意此时是true
console.log(123 === 123n) // false

console.log("123" == 123) // true
console.log("123" === 123) // false

console.log(null == undefined) // true
console.log(null === undefined) // false

// 注意:
NaN 的数据类型是 number
数组(Array)的数据类型是 object
日期(Date)的数据类型为 object
null 的数据类型是 object
未定义变量的数据类型为 undefined



constructor

"John".constructor                 // 返回函数 String()  { [native code] }
(3.14).constructor                 // 返回函数 Number()  { [native code] }
false.constructor                  // 返回函数 Boolean() { [native code] }
[1,2,3,4].constructor              // 返回函数 Array()   { [native code] }
{name:'John', age:34}.constructor  // 返回函数 Object()  { [native code] }
new Date().constructor             // 返回函数 Date()    { [native code] }
function () {}.constructor         // 返回函数 Function(){ [native code] }

// 1
function isArray(myArray) {
    return myArray.constructor.toString().indexOf("Array") > -1;
}

function isDate(myDate) {
    return myDate.constructor.toString().indexOf("Date") > -1;
}

// 2
var test=new Array();
if (test.constructor==Array)
{
document.write("This is an Array");
}
if (test.constructor==Boolean)
{
document.write("This is a Boolean");
}
if (test.constructor==Date)
{
document.write("This is a Date");
}
if (test.constructor==String)
{
document.write("This is a String");
}

// 3
Object.prototype.toString.call('123') === '[object String]' // true
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call("abc");// "[object String]"
Object.prototype.toString.call(123);// "[object Number]"
Object.prototype.toString.call(true);// "[object Boolean]"



分类

  • 原始数据类型

    • boolean
    • number
    • string
    • null
    • undefined
    • bigint
  • 对象类型,有时也被称为

    引用类型

    • 简单对象类型(Object)
    • 数组(Array)

    • 函数(Function)

    • 日期(Date)

    • 正则表达式对象(RegExp)

      ,以

      /

      开始

      /

      结束
    • 包装类型
  • NaN表示not a number

  • +Infinity表示正无穷大

  • -Infinity表示负无穷大

  • number表示的数字参与运算会存在数据

    精度

    问题,

    52位

  • null空类型只能取一个值null,表示对象未初始化,它和空对象{}不一样

  • undefined未定义类型表示一个变量申明时

    未初始化


  • bigint

    表示任意精度的整数,末尾加上

    n


  • 不能

    和普通数值类型混合运算,但可以比较

  • bigint

    不能

    用于Math对象中的方法



string

字符串转成数组

txt="a,b,c,d,e"   // String
txt.split(",");   // 使用逗号分隔
txt.split(" ");   // 使用空格分隔
txt.split("|");   // 使用竖线分隔 
 var str = '改革春风吹满地,春天来了';
console.log(str.indexOf('春')); //默认从0开始查找 ,结果为2
console.log(str.indexOf('春', 3)); // 从索引号是 3的位置开始往后查找,结果是8


// charAt
var str = 'abcoefoxyozzopp';
    var o = {};
    for (var i = 0; i < str.length; i++) {
        var chars = str.charAt(i); // chars 是 字符串的每一个字符
        if (o[chars]) { // o[chars] 得到的是属性值
            o[chars]++;
        } else {
            o[chars] = 1;
        }
    }
    console.log(o);
    // 2. 遍历对象
    var max = 0;
    var ch = '';
    for (var k in o) {
        // k 得到是 属性名
        // o[k] 得到的是属性值
        if (o[k] > max) {
            max = o[k];
            ch = k;
        }
    }
    console.log(max);
    console.log('最多的字符是' + ch);

其他方法和java的类似,indexOf 从1开始

属性:length,prototype

方法名 说明

concat(str1,str2,str3…) concat() 方法用于连接两个或对各字符串。拼接字符串

substr(start,length) 从 start 位置开始(索引号), length 取的个数。

slice(start,end) 从 start 位置开始,截取到 end 位置 ,end 取不到 (两个都是索引号)

substring(start,end) 从 start 位置开始,截取到 end 位置 ,end 取不到 (基本和 slice 相同,但是不接受负)


  • toUpperCase()

    转换大写

  • toLowerCase()

    转换小写



Number

  • 默认情况下,JavaScript 数字为十进制显示
  • 安全整数范围为

    -(253 - 1)到


    253 - 1

    之间的整数,包含

    -(253 - 1)和


    253 - 1

  • 但是你可以使用 toString() 方法 输出16进制、8进制、2进制。
var myNumber=128;
myNumber.toString(16);   // 返回 80
myNumber.toString(8);    // 返回 200
myNumber.toString(2);    // 返回 10000000

Number.isInteger(10);        // 返回 true Number.isInteger(10.5);      // 返回 false
Number.isSafeInteger(10);    // 返回 true
Number.isSafeInteger(12345678901234567890);  // 返回 false
Number.isNaN()

var num = 5.56789;
var n=num.toExponential()
n 输出结果:
5.56789e+0

把数字格式化为指定的长度:
var num = new Number(13.3714);
var n=num.toPrecision(2);
n 输出结果:
13

把数字转换为字符串,结果的小数点后有指定位数的数字:
var num = 5.56789;
var n=num.toFixed(2);
n 输出结果:
5.57

属性:MAX_VALUE,MIN_VALUE,prototype,NEGATIVE_INFINITY 负无穷大,POSITIVE_INFINITY正无穷大,NAN



Array

  • 数组Array,属性length获取个数,里面的元素可以

    任意类型
  • join(sep)拼接
  • split(sep)分割字符串
  • sort()
  • push()向后添加
  • remove(index, count),index位置开始删除count个元素
  • isArray()
  • 其他参考网上
var person = [];
person[0] = "John";
person[1] = "Doe";
person[2] = 46;
var x = person.length;         // person.length 返回 3
var y = person[0];             // person[0] 返回 "John"	

var person = [];
person["firstName"] = "John";
person["lastName"] = "Doe";
person["age"] = 46;
var x = person.length;         // person.length 返回 0
var y = person[0];             // person[0] 返回 undefined

forEach(value, index, array)

<p>乘以: <input type="number" id="multiplyWith" value="10"></p>
<button onclick="numbers.forEach(myFunction)">点我</button>
<p>计算后的值: <span id="demo"></span></p>
<script>
var numbers = [65, 44, 12, 4];

function myFunction(item,index,arr) {
    arr[index] = item * document.getElementById("multiplyWith").value;
    demo.innerHTML = numbers;
}
</script>

属性:length,prototype



Date

<script>
    var timestamp = new Date();
    document.write('<font size="2" ><B>' + timestamp.getFullYear()
      + "年" + (timestamp.getMonth() + 1) + "月"
      + timestamp.getDate()+ "日"+ '</B></font><BR>');
 
    var hours;
    var mins;
    var time;
    hours = timestamp.getHours();
    if (hours >= 12)
      time = " 下午";
    else	
      time = " 上午";
    if (hours > 12) hours -= 12;
    if (hours == 0) hours = 12;
    mins = timestamp.getMinutes();
    if (mins < 10) mins = "0" + mins;
    document.write('<font size="2"><B>' + time
      + hours + ":"+ mins  + '</B></font>');
  </script>
var date = new Date();
console.log(date.getFullYear()); // 返回当前日期的年 2019
console.log(date.getMonth() + 1);  //返回的月份小一个月 记得月份 +1
console.log(date.getDate); //返回的是几号
console.log(date.getDay());  //周一返回1 周6返回六 周日返回0



// 写一个 2019年 5月 1日 星期三
var date = new Date(); 
var year =  date.getFullYear();
var month = date.getMonth() + 1;
var dates = date.getDate();
console.log('今天是' + year +'年' + month + '月' + dates +'日' );

// 封装一个函数返回当前的时分秒 格式 08:08:08
function getTimer() {
    var time = new Date();
    var h = time.getHours();
    h = h < 10 ? '0' + h : h;
    var m = time.getMinutes();
    m = m < 10 ? '0' + m : m;
    var s = time.getSeconds();
    s = s < 10 ? '0' + s : s;
    return h + ':' + m + ':' + s;
}
console.log(getTimer());


// 倒计时
function countDown(time) {
    var nowTime = +new Date(); //没有参数,返回的是当前时间总的毫秒数
    var inputTime = +new Date(time); // 有参数,返回的是用户输入时间的总毫秒数
    var times = (inputTime - nowTime) / 1000; //times就是剩余时间的总的秒数

    var d = parseInt(times / 60 / 60 / 24); //天数
    d < 10 ? '0' + d : d;
    var h = parseInt(times / 60 / 60 % 24); //小时
    h < 10 ? '0' + h : h;
    var m = parseInt(times / 60 % 60); //分
    m < 10 ? '0' + m : m;
    var s = parseInt(times % 60); //秒
    s < 10 ? '0' + s : s;
    return d + '天' + h + '时' + m + '分' + s + '秒';
}
console.log(countDown('2020-11-09 18:29:00'));
var date = new Date;
console.log(date); //现在时间



Math

  • round 四舍五入
  • random随机
  • floor向下取整
  • ceil向上取整
  • 。。其他和java类似
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Math对象</title>
  <style>
    #display{ background-color:red; color:white; font-size:16px; font-weight:bold;}
  </style>
</head>
<body>
 <input type="text" id="myIn">
 <input type="button" value="猜数" id="btn" />
 <label id="display"></label>
<script>
  var myrandom=Math.floor(Math.random()*100);
 
  var myBtn=document.getElementById("btn");
  var myInput=document.getElementById("myIn");
  var myDisplay=document.getElementById("display");
 
  myBtn.onclick=function(){
	  myValue=myInput.value;
	  if(isNaN(myValue)) 
      myDisplay.innerHTML="请0-100输入数字";
	  else{
      if(myValue>myrandom) 
        myDisplay.innerHTML="数据大了!";
      else if(myValue<myrandom) 
        myDisplay.innerHTML="数据小了!";
      else 
        myDisplay.innerHTML="恭喜您,猜对了!";
	  }
  }
</script>
</body>
</html>



运算符

和java的类型类似

注意: type=“text”

<input type="text" name="pagenum" id="pagenum"><input type="button" value="翻页" onclick="o()">
    
<script>
function o() {
    var pageText = document.querySelector("#pagenum");
    var pageNum = pageText.value;
  	// 字符串转为 Number
    console.log(window.parseInt(pageNum) + 1)
}
</script>



boolean

undifined -> false
null -> false
0 -> false
'' -> false
false -> false
// 其他的值都是真,但要注意 [],{}是真



Boolean

var b1=new Boolean(0);
var b2=new Boolean(1);
var b3=new Boolean("");
var b4=new Boolean(null);
var b5=new Boolean(NaN);
var b6=new Boolean("false");
document.write("0 为布尔值 "+ b1 +"<br>");
document.write("1 为布尔值 "+ b2 +"<br>");
document.write("空字符串是布尔值 "+ b3 + "<br>");
document.write("null 是布尔值 "+ b4+ "<br>");
document.write("NaN 是布尔值 "+ b5 +"<br>");
document.write("字符串'false' 是布尔值"+ b6 +"<br>");

0 为布尔值 false
1 为布尔值 true
空字符串是布尔值 false
null 是布尔值 false
NaN 是布尔值 false
字符串'false' 是布尔值true



浮点型

var x = 0.1;
var y = 0.2;
var z = x + y            // z 的结果为 0.30000000000000004
if (z == 0.3)            // 返回 false
var z = (x * 10 + y * 10) / 10;       // z 的结果为 0.3

精度。。。



全局属性/函数

Infinity 代表正的无穷大的数值
NaN 指示某个值是不是数字值
undefined 指示未定义的值

decodeURI()
解码某个编码的 URI。
decodeURIComponent() 解码一个编码的 URI 组件。

encodeURI()
把字符串编码为 URI。
encodeURIComponent() 把字符串编码为 URI 组件。
escape() 对字符串进行编码。
eval() 计算 JavaScript 字符串,并把它作为脚本代码来执行。
isFinite() 检查某个值是否为有穷大的数。

isNaN()
检查某个值是否是数字。

Number()
把对象的值转换为数字。

parseFloat()
解析一个字符串并返回一个浮点数。

parseInt()
解析一个字符串并返回一个整数。

String()
把对象的值转换为字符串。
unescape() 对由 escape() 编码的字符串进行解码。

https://www.runoob.com/jsref/jsref-obj-global.html



Error

try {
    adddlert("Welcome");
}
catch(err) {
    document.getElementById("demo").innerHTML = 
    err.name + "<br>" + err.message;
}



类型转换

// 转成 字符串*****************************
String(x)         // 将变量 x 转换为字符串并返回
String(123)    // 将数字 123 转换为字符串并返回
String(100 + 23) // 将数字表达式转换为字符串并返回
x.toString()
(123).toString()
(100 + 23).toString()

String(false)        // 返回 "false"
String(true)         // 返回 "true"
false.toString()     // 返回 "false"
true.toString()      // 返回 "true"

// 字符串转成数字******************************
Number("3.14")    // 返回 3.14
Number(" ")       // 返回 0
Number("")        // 返回 0
Number("99 88")   // 返回 NaN
Number(false)     // 返回 0
Number(true)      // 返回 1
parseFloat()	// 解析一个字符串,并返回一个浮点数
parseInt()	// 解析一个字符串,并返回一个整数

var y = "5";      // y 是一个字符串
var x = + y;      // x 是一个数字
var y = "5";      // y 是一个字符串
var x = + y;      // x 是一个数字

d = new Date();
d.getTime()        // 返回 1404568027739

// 自动类型转换*********************************
5 + null    // 返回 5         null 转换为 0
"5" + null  // 返回"5null"   null 转换为 "null"
"5" + 1     // 返回 "51"      1 转换为 "1" 
"5" - 1     // 返回 4         "5" 转换为 5

myVar = {name:"Fjohn"}  // toString 转换为 "[object Object]"
原始值 转为number 转为字符串 转为boolean
false 0 “false” false
true 1 “true” true
0 0 “0” false
1 1 “1” true

“0”
0 “0”
true

“000”

=
0 “000”
true

=
“1” 1 “1” true
NaN NaN “NaN” false
Infinity Infinity “Infinity” true
-Infinity -Infinity “-Infinity” true

“”

0
“”
false
“20” 20 “20” true
“Runoob” NaN “Runoob” true

[ ]

0

“”

true
[20] 20 “20” true
[10,20] NaN “10,20” true
[“Runoob”] NaN “Runoob” true

[“Runoob”,“Google”]
NaN
“Runoob,Google”
true
function(){} NaN “function(){}” true

{ }
NaN
“[object Object]”

true
null 0 “null” false
undefined NaN “undefined” false



简单 复杂类型

https://juejin.cn/post/6844903813858459656

简单类型又叫做基本数据类型或者值类型,复杂类型又叫做引用类型。

值类型:简单数据类型/基本数据类型,在存储时变量中存储的是值本身,因此叫做值类型

string ,number,boolean,undefined,null

引用类型:复杂数据类型,在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型

通过 new 关键字创建的对象(系统对象、自定义对象),如

Object、Array、Date、Function、正则

  • 简单数据类型存放到栈里面
  • 复杂数据类型存放到堆里面
<script>
    // 简单数据类型传参
    function fn(a) {
        a++;
        console.log(a);
    }
    var x = 10;
    fn(x);
    console.log(x);
</script>
<script>
    // 复杂数据类型传参
    function Person(name) {
        this.name = name;
    }

    function f1(x) { // x = p
        console.log(x.name); // 2. 这个输出什么 ?  刘德华   
        x.name = "张学友";
        console.log(x.name); // 3. 这个输出什么 ?   张学友
    }
    var p = new Person("刘德华");
    console.log(p.name); // 1. 这个输出什么 ?   刘德华 
    f1(p);
    console.log(p.name); // 4. 这个输出什么 ?   张学友
</script>



JS函数

  • 在JavaScript中函数会被提前申明,无论在当前作用域中什么地方写的函数,他们全会被移动到当前作用域开始之处



使用

  • 函数一等公民,没有重载的概念


  • 函数没有写返回值,默认return undefined

  • 调用的时候看是不是直接赋值,fun() 有返回值, fun 没有返回值

function show(a) {
    console.log(a)
}
show(2, 2, 3, 4) // 只会打印第一个 2

function fn(){
    console.log(arguments)
 }
 fn(1, 2, 3, 4);



// ******实现重载的操作*******
// 1.数据类型不同的方式
function show(a) {
    if(typeof(a) === 'string') {
        return a.length;
    } else if (typeof(a) === 'number') {
        return a + 1;
    }
}
console.log(show(1))
console.log(show("a"))

// 2.数量
function info(a, b, c) {
    if(b === undefined) {
        console.log("一个值");
    } else {
        if(c === undefined) {
            console.log("两个值");
        } else {
            console.log("三个值");
        }
    }
}
info(1) 
info(1, 2)
info(1, 2, 3)

// 这种方式会有小bug,就是传入的是undefined的时候
info(1, undefined, 3) // 一个值



arguments

// 函数声明
function fn() {
    console.log(arguments);  //里面存储了所有传递过来的实参
    console.log(arrguments.length); // 3
    console.log(arrguments[2]); // 3
}

// 函数调用
fn(1,2,3);



匿名函数

定义之后无法再次调用,可以将匿名函数

赋值给某个变量或者作为参数

传给其他函数

// 方式一
console.log(function(a, b) {
    return a + b;
}(1, 2));

// 方式二
var fun = function (a, b) {
    return a + b;
}
console.log(fun(1, 2));

(function (){
    console.log("666"); 
})()



脱离与绑定

一个对象的函数 赋给了另一个 变量,会从该对象 脱离

function person(name) {
    this.name = name || "张三"
    this.info = function() {
        console.log(this)
        console.log(this.name)
    }
}

var p1 = new person()
var fun = p1.info
p1.info()

// 脱离对象
fun()

// 绑定回 原来的对象
var fun2 = fun.bind(p1)
fun2()
var p2 = new person("李四")

var fun3 = fun.bind(p2)
fun3();

函数预解析(函数提升)

函数提升: 函数的声明会被提升到当前作用域的最上面,但是不会调用函数。

fn();				//11
function fn() {
    console.log('11');
}

解决函数表达式声明调用问题

对于函数表达式声明调用需要记住:函数表达式调用必须写在函数声明的下面

// 匿名函数(函数表达式方式):若我们把函数调用放在函数声明上面
fn();
var  fn = function() {
    console.log('22'); // 报错
}

//相当于执行了以下代码
var fn;
fn();      //fn没赋值,没这个,报错
var  fn = function() {
    console.log('22'); //报错
}



作用域

// 此处不能调用 carName 变量
function myFunction() {
    var carName = "Volvo";
    // 函数内可调用 carName 变量
}

// 此处不能调用 carName 变量
function myFunction() {
    var carName = "Volvo";
    // 函数内可调用 carName 变量
}

如果变量在函数内没有声明(没有使用 var 关键字),该变量为全局变量。
// 此处不能调用 carName 变量
function myFunction() {
    var carName = "Volvo";
    // 函数内可调用 carName 变量
}

局部变量在函数执行完毕后销毁
全局变量在页面关闭后销毁


作用域链

作用域的集合就是作用域链==(子集可以访问父集,父集不能访问子集)==

function fn(){
    var a=10;
    function fn1(){
        var b=20;
        alert(a) //10
        function fn2(){
            alert(b) //20
            alert(a) //10  子集可以跨级访问父级
        }
        fn2()
    }
    fn1()
}
fn()

在运行期上下文的作用域链中,标识符所在的位置越深,读写速度就会越慢。因此在标识符解析的时候,

查找全局变量是最慢的

。所以,在编写代码的时候应

尽量少使用全局变量,尽可能使用局部变量



闭包**

优缺点,引起内存泄露的解决

JavaScript 支持嵌套函数。嵌套函数可以访问上一层的函数变量。

该实例中,内嵌函数

plus()

可以访问父函数的

counter

变量

function add() {
    var counter = 0;
    function plus() {counter += 1;}
    plus();    
    return counter; 
}

如果我们能在外部访问

plus()

函数,这样就能解决计数器的困境。

我们同样需要确保

counter = 0

只执行一次。

var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();
 
add();
add();
add();
// 计数器为 3
// add变量可以作为一个函数使用。非常棒的部分是它可以访问函数上一层作用域的计数器。这个叫作 JavaScript 闭包。它使得函数拥有私有变量变成可能。计数器受匿名函数的作用域保护,只能通过 add 方法修改。



JS对象



创建对象

  • 利用字面量创建对象
  • 利用 new Object创建对象
  • 利用构造函数创建对象
// 常用
var person = {
    firstName : "a",
    lastName : "b",
    id : 12,
    fullName : function() {
        return this.fristName + " " + this.lastName
    } 
}
// this 是person
console.log(person)
console.log(person.fullName)

var persons = {
    name : "卢总",
    hobbies : ['篮球', '唱K', 'rap'],
    dog : {name : '吗捞', color : '黑色'},
    eat : function(food) {
        console.log('喜欢吃' + food)
    }
}
console.log(persons.eat('shit'))

var star = {
    name : 'pink',
    age : 18,
    sex : '男',
    sayHi : function(){
        alert('大家好啊~');
    }
};
// 多个属性或者方法中间用逗号隔开
// 方法冒号后面跟的是一个匿名函数

var obj = new Object(); //创建了一个空的对象
obj.name = '张三丰';
obj.age = 18;
obj.sex = '男';
obj.sayHi = function() {
    console.log('hi~');
}

//1.我们是利用等号赋值的方法添加对象
//2.每个属性和方法之间用分号结束
console.log(obj.name);
console.log(obj['sex']);
obj.sayHi();

要注意

脱离与绑定

// 用的少
function Dog(name, color) {
    this.name = name;
    this.color = color;
}
var dog = new Dog("卢总", "黑色")
console.log(dog)



new 过程**

  1. 在内存中创建一个新的空对象。
  2. 让 this 指向这个新的对象。
  3. 执行构造函数里面的代码,给这个新对象添加属性和方法
  4. 返回这个新对象(所以构造函数里面不需要return)

模拟js new 的过程

function _new(){
  // 1、创建一个新对象
  let target = {};
  let [constructor, ...args] = [...arguments];  // 第一个参数是构造函数
  // 2、原型链连接
  target.__proto__ = constructor.prototype;
  // 3、将构造函数的属性和方法添加到这个新的空对象上。
  let result = constructor.apply(target, args);
  if(result && (typeof result == "object" || typeof result == "function")){
    // 如果构造函数返回的结果是一个对象,就返回这个对象
    return result
  }
  // 如果构造函数返回的不是一个对象,就返回创建的新对象。
  return target
}
let p2 = _new(Person, "小花")
console.log(p2.name)  // 小花
console.log(p2 instanceof Person) // true



对象添加属性

obj.name = “Kitty”; //为对象增加属性

obj.showInfo = function () { //为对象添加方法
    console.log(this.name + "," + this.age);
};

通过对象名.方法名的方式 调用改方法

obj.showInfo(); //调用对象的方法

  • Javascript中

    不存在

    公有方法和私有方法的

    区别
  • 一般使用

    下划线

    开头的变量名或函数名作为私有成员的标志,_name
  • 是否会被调用,只能

    靠自觉

遍历对象



polyfill**


兜底

默认值

var person = {
    name: "咋"
}
var person2 = {
    name: "li",
    age: 21
}
person.age = person.age || 18
person2.age = person2.age || 18  // 有值则用有的,没有则用||后面的值
console.log(person) // 18 
console.log(person2)// 21

什么是兜底?

  • es6的代码 使用 babel 变为es5的代码
var person = {
    name: "咋"
}
person.info = function() {
    console.log(this)
}
var person2 = {
    name: "li",
    age: 21
}

function show(person) {
    person.info && person.info();
}
// 等同于
// function show(person) {
//     if(person.info != undefined) {
//         person.info()
//     }
// }
show(person);
show(person2);// 没有,所以不会调用函数



prototype**

所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法:


  • Date

    对象从

    Date.prototype

    继承

  • Array

    对象从

    Array.prototype

    继承

  • Person

    对象从

    Person.prototype

    继承

所有 JavaScript 中的对象都是

位于原型链顶端的 Object 的实例

JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在

该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索

,直到找到一个名字匹配的属性或到达原型链的末尾。


Date

对象,

Array

对象, 以及

Person

对象从

Object.prototype

继承

  • 通过原型

    向对象添加必要的属性和方法
  • 这种方法添加的属性和方法属于对象,

    每个对象实例的属性值和方法都是相同的
function person(name, age) {
    this.age = age || 18
    this.name = name || "李四"
    this.info = function() {
        console.log(this)
    }
}

person.prototype.nationality = "China"
var a1 = new person()
console.log(a1.nationality)
a1.info();

var a = [1, 2, 3]
// polyfill
Array.prototype.crazy = Array.prototype.crazy || function() {
    console.log("sss");
}



内置对象

https://www.runoob.com/jsref/prop-element-children.html

https://developer.mozilla.org/zh-CN/



js中的堆栈

demo1.
var a = 1;
var b = a;
b = 2;

// 这时a是? a=1

demo1中在变量对象中的数据发生复制行为时,系统会自动为新的变量分配一个新值。var b = a执行之后,b虽然重新赋值为2,但是他们其实已经是相互独立互不影响的值了。

demo2.
var m = { a: 1, b: 2 }
var n = m;
n.a = 2;

// 这时m.a的值呢? 2



栈内存

栈内存由编译器自动分配与释放。我们可以直接操作栈内存中的值。js中的基本数据类都有固定的大小,被分配到栈内存中。这些基本类型的值都是按值引用。

将一个基本类型的值赋值给另外一个基本类型时,会为这个新的值重新创建一个值并保存在栈内存中



堆内存

堆内存是

链表结构的类型

,可以动态分配大小,js引用类型占用内存空间的大小不固定,存储在堆内存中。由于JS不允许直接访问堆内存中的位置,因此我们不能直接操作js的引用类型。而是生成一个指针,并将它放到栈内存中,通过这个

指针来操作引用类型



垃圾回收

现在

浏览器基本都在使用标记-清除的算法来执行垃圾回收

。而不是使用引用计数的方式,因为引用计数方式无法释放循环引用结构的内存占用。 标记清除算法的核型概念是:从根部(在JS中就是全局对象)出发定时扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的。那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收



内存泄漏

对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。 对于不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)



常见的内存泄漏

  1. 不合理的计时器
  2. 被共享的闭包作用域
  3. 脱离dom引用
  4. 意外的全局变量



this

  • 每次使用this都要

    问自己这个this是谁

    !!!!!!
  • 对象. this为该对象,每次点的时候 要注意



方法中

var person = {
  firstName: "John",
  lastName : "Doe",
  id       : 5566,
  fullName : function() {
    return this.firstName + " " + this.lastName;
  }
};

this是person



单独使用

var x = this;

// 或者 严格模式下
"use strict";
var x = this;

this都是window对象



函数中

function myFunction() {
    return this;
}
this是window对象

// 严格模式下
"use strict";
function myFunction() {
  return this;
}

thisundefined



cookie

function setCookie(cname,cvalue,exdays){
    var d = new Date();
    d.setTime(d.getTime()+(exdays*24*60*60*1000));
    var expires = "expires="+d.toGMTString();
    document.cookie = cname+"="+cvalue+"; "+expires;
}
function getCookie(cname){
    var name = cname + "=";
    var ca = document.cookie.split(';');
    for(var i=0; i<ca.length; i++) {
        var c = ca[i].trim();
        if (c.indexOf(name)==0) { return c.substring(name.length,c.length); }
    }
    return "";
}
function checkCookie(){
    var user=getCookie("username");
    if (user!=""){
        alert("欢迎 " + user + " 再次访问");
    }
    else {
        user = prompt("请输入你的名字:","");
          if (user!="" && user!=null){
            setCookie("username",user,30);
        }
    }
}



in place

arr.sort()
arr.push()
// 1.push() 在我们数组的末尾,添加一个或者多个数组元素 push 推
var arr = [1, 2, 3];
arr.push(4, '秦晓');
console.log(arr);
console.log(arr.push(4, '秦晓'));
console.log(arr);
// push 完毕之后,返回结果是新数组的长度


// 2. unshift 在我们数组的开头 添加一个或者多个数组元素
arr.unshift('red');
console.log(arr);

// pop() 它可以删除数组的最后一个元素,一次只能删除一个元素
arr.pop(); //不加参数
// shift() 它剋删除数组的第一个元素,一次只能删除一个元素
arr.shift(); //不加参数



非 in place


会产生新的

str = str.replace(",", "-")
arr = arr.join("-");
arr.concat



遍历 ***

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

// 2
for (var element in arr) {
    console.log(arr[element])
}

// 3
for (var element of arr) {
    console.log(element)
}

// 4
arr.forEach(function(i) {
    console.log(i);
})

// 4
arr.forEach((e) => console.log(e));

数组不是下标的情况

var arr2 = [1, 4, 3]
arr[5000] = 5000;
console.log(arr[5000]); // 5000
console.log(arr)
// (5001) [1, 2, 1, empty × 4997, 5000, name: "apple", taste: "dd"]

// of,forEach遍历不出来,只能用fori 和 in遍历
arr["aname"] = "apple"
arr["btaste"] = "dd"
console.log(arr["aname"])
console.log(arr["btaste"])



void

<a href="javascript:void(0);">点我没有反应的!</a>
<a href="#pos">点我定位到指定位置!</a>
<a href="javascript:void(alert('Warning!!!'))">点我!</a>



JSON

键的字符串的引号”” 可以

省略

var person = {
    "name": "张三",
    "age": 18,
    "dog": {
        dogname: "卢总",
        dogcolor: "黄色",
    },
    arr: ["sdfsd","sdfsd",1 ,{tel:"123456", tel1: 123}]
}
console.log(person)
console.log(person.arr[3].tel)

属性值可以使用变量

属性的名字,使用变量值,使用[]

var name2 = "李四"
var fied = "name"
var p2 = {
    [filed] :name2
}
console.log(p2)

如果

键的名



变量名

相同,键名可以省略

// 如果键的名i在和变量名相同,键名可以省略

var value = 3

var id = 5
var v1 = {
    "value": value,
    "id": id,
    "info": function info() {
        console.log(this)
    }
}
console.log(v1)

// 等同于
var v2 = {
    value,
    id,
    info() {
        console.log(this)
    },
}
console.log(v2)



删除

myObj = {
    "name":"runoob",
    "alexa":10000,
    "sites": {
        "site1":"www.runoob.com",
        "site2":"m.runoob.com",
        "site3":"c.runoob.com"
    }
}
delete myObj.sites.site1



json数组

{
"name":"网站",
"num":3,
"sites":[ "Google", "Runoob", "Taobao" ]
}
for (i in myObj.sites) {
    x += myObj.sites[i] + "<br>";
}



json.parse()


后端传给前端json,前端将数据显示

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>

<h2>json转为js对象</h2>
<p id="demo"></p>
<script>
var text = '{ "sites" : [' +
	'{ "name":"Runoob" , "url":"www.runoob.com" },' +
	'{ "name":"Google" , "url":"www.google.com" },' +
	'{ "name":"Taobao" , "url":"www.taobao.com" } ]}';
obj = JSON.parse(text);
document.getElementById("demo").innerHTML = obj.sites[1].name + " " + obj.sites[1].url;
</script>

</body>
</html>



json.stringify()

前端对象转成json传给后端,

var obj = { "name":"runoob", "alexa":10000, "site":"www.runoob.com"};
JSON.stringify(arr);

数组转换 变成json传给后端

var arr = [ "Google", "Runoob", "Taobao", "Facebook" ];
var myJSON = JSON.stringify(arr);



异步

xhr,183

ajax

promise,具体看es6



jsonp

可以

跨域获取数据

$.getJSON('url', function(data){
	...
})



正则

https://www.runoob.com/jsref/jsref-obj-regexp.html

修饰符:

修饰符 描述

i
执行对大小写不敏感的匹配。

g
执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m 执行多行匹配。

方法:


exec
检索字符串中指定的值。返回找到的值,并确定其位置。

test
检索字符串中指定的值。返回 true 或 false。

toString
返回正则表达式的字符串。
var str="Hello world!";
//查找"Hello"
var patt=/Hello/g;
var result=patt.exec(str);
document.write("返回值: " +  result); 
//查找 "RUNOOB"
patt=/RUNOOB/g;
result=patt.exec(str);
document.write("<br>返回值: " +  result);
以上实例输出结果:

Returned value: Hello
Returned value: null

支持正则的String的方法


search
检索与正则表达式相匹配的值。 1 4

match
找到一个或多个正则表达式的匹配。 1 4

replace
替换与正则表达式匹配的子串。 1 4

split
把字符串分割为字符串数组。
  • 正则表达式 以/开始 /结束
  • .表示 一个字符
  • *表示 0或多个字符
  • +表示 重复前面的字符,有一个或多个字符
  • ^表示 开头 /^(aa)/ 以aa开头

  • $

    表示 结尾 /(bc)$/ 以bc结尾

search找不到返回 -1, 找到则,表示从哪个下标开始

search找

完全匹配上

var str = "abc"
var reg1 = /abcdefg/
var reg2 = /abc/ 
console.log(str.search(reg1)) // -1 找不到
console.log(str.search(reg2)) // 0 从0下标开始
var str = "zzabczz"
var str2 = "zzabbczz"
var reg1 = /a.{1,2}c/ // 表示有ac中间有1个或两个字符
console.log(str.search(reg1)) // 2 从2下标开始
console.log(str2.search(reg1)) // 2

var reg2 = /^a.{1,2}c$/ // 表示开头是a ,结尾是c,中间一个或两个字符

var reg3 = /^a.*c$/ // 表示开头a,结尾b,重复前面的字符,有0个或多个字符
var str4 = "ac"
var reg4 = /^a.+c$/
console.log(str4.search(reg4)) // 0
var reg4 = /^a.+c$/ // 表示开头a,结尾b, +号是重复前面的字符,一个或多个
var str4 = "ac"
var reg4 = /^a.+c$/
console.log(str4.search(reg4)) // -1

// 表示开头a,结尾b,中间是a,或者b,或者c ,如果是连续的 直接用-
var reg5 = /^a[a|b|c]+c$/
var reg6 = /^a[a-c]+c$/

具体可参考网上的



事件

onclick,鼠标点击

<h1 onclick="this.innerHTML='Ooops!'">点击文本!</h1>

<!DOCTYPE html>
<html>
<head>
<script>
function changetext(id)
{
    id.innerHTML="Ooops!";
}
</script>
</head>
<body>
<h1 onclick="changetext(this)">点击文本!</h1>
</body>
</html>

onmouseover,onmouseout

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>

<div onmouseover="mOver(this)" onmouseout="mOut(this)" style="background-color:#D94A38;width:120px;height:20px;padding:40px;">Mouse Over Me</div>
<script>
function mOver(obj){
	obj.innerHTML="Thank You"
}
function mOut(obj){
	obj.innerHTML="Mouse Over Me"
}
</script>

</body>
</html>

onmousedown、onmouseup

onload,可用于处理cookie

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body onload="checkCookies()">

<script>
function checkCookies(){
	if (navigator.cookieEnabled==true){
		alert("Cookies 可用")
	}
	else{
		alert("Cookies 不可用")
	}
}
</script>
<p>弹窗-提示浏览器 cookie 是否可用。</p>
</body>
</html>

onfocus

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<head>
<script>
function myFunction(x){
	x.style.background="yellow";
}
</script>
</head>
<body>

输入你的名字: <input type="text" onfocus="myFunction(this)">
<p>当输入框获取焦点时,修改背景色(background-color属性) 将被触发。</p>

</body>
</html>



事件



操作元素



元素内容
var div = document.querySelector('div');
div.innerText = '<strong>今天是:</strong> 2019';
// 2. innerHTML 识别html标签 保留空格和换行的
div.innerHTML = '<strong>今天是:</strong> 2019';
<script>
    function setInnerText(element, text) {
        //判断浏览器是否支持这个属性
        if (typeof element.textContent == "undefined") {//不支持
            element.innerText = text;
        } else {//支持这个属性
            element.textContent = text;
        }
    };
</script>

innerHTML,innerText,textConte



元素属性
// img.属性
img.src = "xxx";

input.value = "xxx";
input.type = "xxx";
input.checked = "xxx";
input.selected = true / false;
input.disabled = true / false;


样式属性

行内样式

// element.style
div.style.backgroundColor = 'pink';
div.style.width = '250px';

类名样式

element.className

.style优先级高于className

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>

  <style>
    .firstChange {
      background-color: red;
    }
  </style>
</head>

<body>


  <div class="first">文本</div>
  <script>
    // 1. 使用 element.style 获得修改元素样式  如果样式比较少 或者 功能简单的情况下使用
    var test = document.querySelector('div');
    test.onclick = function () {
      // this.style.backgroundColor = 'purple';
      // this.style.color = '#fff';
      // this.style.fontSize = '25px';
      // this.style.marginTop = '100px';
      // 让我们当前元素的类名改为了 change

      // 2. 我们可以通过 修改元素的className更改元素的样式 适合于样式较多或者功能复杂的情况
      // 3. 如果想要保留原先的类名,我们可以这么做 多类名选择器
      // this.className = 'change';
      this.className = 'firstChange';
    }
  </script>

</body>

</html>


排他
<body>
    <button>按钮1</button>
    <button>按钮2</button>
    <button>按钮3</button>
    <button>按钮4</button>
    <button>按钮5</button>
    <script>
        // 1. 获取所有按钮元素
        var btns = document.getElementsByTagName('button');
        // btns得到的是伪数组  里面的每一个元素 btns[i]
        for (var i = 0; i < btns.length; i++) {
            btns[i].onclick = function() {
                // (1) 我们先把所有的按钮背景颜色去掉  干掉所有人
                for (var i = 0; i < btns.length; i++) {
                    btns[i].style.backgroundColor = '';
                }
                // (2) 然后才让当前的元素背景颜色为pink 留下我自己
                this.style.backgroundColor = 'pink';

            }
        }
        //2. 首先先排除其他人,然后才设置自己的样式 这种排除其他人的思想我们成为排他思想
    </script>
</body>



自定义属性
<body>
    <div id="demo" index="1" class="nav"></div>
    <script>
        var div = document.querySelector('div');
        // 1. 获取元素的属性值
        // (1) element.属性
        console.log(div.id);
        //(2) element.getAttribute('属性')  get得到获取 attribute 属性的意思 我们程序员自己添加的属性我们称为自定义属性 index
        console.log(div.getAttribute('id'));
        console.log(div.getAttribute('index'));
        // 2. 设置元素属性值
        // (1) element.属性= '值'
        div.id = 'test';
        div.className = 'navs';
        // (2) element.setAttribute('属性', '值');  主要针对于自定义属性
        div.setAttribute('index', 2);
        div.setAttribute('class', 'footer'); // class 特殊  这里面写的就是class 不是className
        // 3 移除属性 removeAttribute(属性)    
        div.removeAttribute('index');
    </script>
</body>



h5自定义属性

H5规定自定义属性 ==data-==开头作为属性名并赋值,使用getAttribute或者

.dataset.

<body>
    <div getTime="20" data-index="2" data-list-name="andy"></div>
    <script>
        var div = document.querySelector('div');
        console.log(div.getAttribute('getTime'));
        div.setAttribute('data-time', 20);
        console.log(div.getAttribute('data-index'));
        console.log(div.getAttribute('data-list-name'));
        // h5新增的获取自定义属性的方法 它只能获取data-开头的
        // dataset 是一个集合里面存放了所有以data开头的自定义属性
        console.log(div.dataset);
        console.log(div.dataset.index);
        console.log(div.dataset['index']);
        // 如果自定义属性里面有多个-链接的单词,我们获取的时候采取 驼峰命名法
        console.log(div.dataset.listName);
        console.log(div.dataset['listName']);
    </script>
</body>



注册事件

<body>
  <div onClick=show()> 注册事件的三种方法 <div>
 <script>
 //1.使用HTML属性注册 
  function show(){
  alert("div被点击了")
  }
 //2.使用javaScipt的DOM注册事件
     var div=document.querySelector("div")
     //注意 事件类型以 on开头 后面对应的事件名为小写
     div.onclick=function(){
     alert("DOM注册点击div")
  }
//3.使用addEventListener()方法注册事件
    var div=document.querySelector("div")
   //向div添加事件
    div.addEventListener('click',()=>{
     alert("Div 注册点击事件")
  })
  //移除div事件
   div.removeEventListener("click",()=>{
       alert("移除div事件")
 })
  <script>
<body>



鼠标事件

鼠标事件 触发条件
onclick 鼠标点击左键触发
onmouseover 鼠标经过触发
onmouseout 鼠标离开触发

onfocus
获得鼠标焦点触发

onblur
失去鼠标焦点触发
onmousemove 鼠标移动触发
onmouseup 鼠标弹起触发
onmousedown 鼠标按下触发


禁止鼠标右键与鼠标选中


  • contextmenu

    主要控制应该何时显示上下文菜单,主要用于程序员取消默认的上下文菜单

  • selectstart

    禁止鼠标选中
<body>
    <h1>我是一段不愿意分享的文字</h1>
    <script>
        // 1. contextmenu 我们可以禁用右键菜单
        document.addEventListener('contextmenu', function(e) {
                e.preventDefault(); // 阻止默认行为
            })
            // 2. 禁止选中文字 selectstart
        document.addEventListener('selectstart', function(e) {
            e.preventDefault();

        })
    </script>
</body>

鼠标事件对象 说明
e.clientX 返回鼠标相对于浏览器窗口可视区的

X

坐标
e.clientY 返回鼠标相对于浏览器窗口可视区的

Y

坐标
e.pageX 返回鼠标相对于文档页面的

X

坐标 IE9+ 支持
e.pageY 返回鼠标相对于文档页面的

Y

坐标 IE9+ 支持
e.screenX 返回鼠标相对于电脑屏幕的 X 坐标
e.screenY 返回鼠标相对于电脑屏幕的Y 坐标
<body>
  <script>
      // 鼠标事件对象 MouseEvent
      document.addEventListener('click', function(e) {
          // 1. client 鼠标在可视区的x和y坐标
          console.log('client---------------------');
          console.log(e.clientX);
          console.log(e.clientY);

          // 2. page 鼠标在页面文档的x和y坐标
          console.log('page---------------------');
          console.log(e.pageX);
          console.log(e.pageY);

          // 3. screen 鼠标在电脑屏幕的x和y坐标
          console.log('screen---------------------');
          console.log(e.screenX);
          console.log(e.screenY);

      })
  </script>
</body>



键盘事件

<script>
  // 常用的键盘事件
  //1. keyup 按键弹起的时候触发 
  document.addEventListener('keyup', function () {
    console.log('我弹起了');
  })

  //3. keypress 按键按下的时候触发  不能识别功能键 比如 ctrl shift 左右箭头啊
  document.addEventListener('keypress', function () {
    console.log('我按下了press');
  })
  //2. keydown 按键按下的时候触发  能识别功能键 比如 ctrl shift 左右箭头啊
  document.addEventListener('keydown', function () {
    console.log('我按下了down');
  })
  // 4. 三个事件的执行顺序  keydown -- keypress -- keyup

  //  5.利用Keycode返回的ASCII码值来判断用户按下的是哪个键
  document.addEventListener("keyup", function (e) {
    if (e.keyCode === 65) {
      alert("你按下的是a键")
    } else {
      alert("你没有按下a键")
    }
  })
</script>

$('#keyword').keypress(function (key1) {
        let key = key1.which; // e.which是按键的值
        // 回车
        if (key == 13) {
            var keyword = $(this).val();
            if (keyword && keyword != '') {
                window.location.href = '/search?keyword=' + keyword;
            }
        }
    });
键盘事件对象

属性
说明
keyCode 返回该



值的ASCII值

  • onkeydown



    onkeyup

    不区分字母大小写,

    onkeypress

    区分字母大小写。
  • 在我们实际开发中,我们更多的使用keydown和keyup, 它能识别所有的键(包括功能键)

  • Keypress

    不识别功能键,但是

    keyCode

    属性能区分大小写,返回不同的ASCII值


一般用 event.key,和 event.code

因为不同电脑,键盘的按键可能不一样



事件冒泡**

比如子元素和父元素都有点击事件,

点了子元素,然后做完会做父元素的点击事件

阻止事件冒泡:


  • event.stopPropagation()

    但不会阻止默认事件==

阻止默认事件

  • event.preventDefault()
<a href="http://www.baidu.com" @click="showInfo">click me</a>

showInfo(event) {
    // 阻止默认行为
    event.preventDefault();
    alert("aa")
}



事件捕获**

先 father,再son

  • document -> html -> body -> father -> son
<body>
    <div class="father">
        <div class="son">son盒子</div>
    </div>
    <script>
        // dom 事件流 三个阶段
        // 1. JS 代码中只能执行捕获或者冒泡其中的一个阶段。
        // 2. onclick 和 attachEvent(ie) 只能得到冒泡阶段。
        // 3. 捕获阶段 如果addEventListener 第三个参数是 true 那么则处于捕获阶段  document -> html -> body -> father -> son
        var son = document.querySelector('.son');
        son.addEventListener('click', function() {
             alert('son');
        }, true);
        var father = document.querySelector('.father');
        father.addEventListener('click', function() {
            alert('father');
        }, true);
    </script>
</body>

先son,再father

  • son -> father ->body -> html -> document
<body>
    <div class="father">
        <div class="son">son盒子</div>
    </div>
    <script>
		// 4. 冒泡阶段 如果addEventListener 第三个参数是 false 或者 省略 那么则处于冒泡阶段  son -> father ->body -> html -> document
        var son = document.querySelector('.son');
        son.addEventListener('click', function() {
            alert('son');
        }, false);
        var father = document.querySelector('.father');
        father.addEventListener('click', function() {
            alert('father');
        }, false);
        document.addEventListener('click', function() {
            alert('document');
        })
    </script>
</body>



事件对象**

兼容性

  • e = e || window.event;
 eventTarget.addEventListener("click",fn)
  function(event){
  }
}
事件对象的属性和方法 说明

e.target
返回

触发

事件的对象 标准
e.srcElement 返回

触发

事件的对象 非标准

ie6-8
e.type 返回事件的

类型

比如 click mouseover 不带 on
e.canceIBubble 该属性阻止冒泡 非标准

ie6-8

使用
e.returnValue 该属性 阻止默认事件(默认行为)非标准

ie6-8

使用 比如不让链接跳转
e.preventDefault() 该方法阻止默认事件(默认行为)标准 比如不让链接跳转
e.stopPropagation() 标准的阻止事件冒泡



this与e.target**


通常情况下e.target 和 this 的指向是一致的

但是有一种情况不同,那就是在事件冒泡时(父子元素有相同事件,单击子元素,父元素的事件处理函数也会被触发执行)

​ 这时候 this 指向的是元素,因为this使终指向的是事件绑定的元素

​ target 指向的是子元素 ,因为target使终指向的是事件触发的元素

    <div>123</div>
    <ul>
        <li>abc</li>
        <li>abc</li>
        <li>abc</li>
    </ul>
     <script>
   1. //target和this指向的一致
        var div = document.querySelector('div');
        div.addEventListener('click', function(e) {
            // e.target 和 this指向的都是div
            console.log(e.target);
            console.log(this);
        });
   2. //在冒泡的情况下 target和this指向不同
     var ul=document.querySelector("ul")
      ul.addEventListener("click"function(e){
         // 我们给ul 绑定了事件  那么this 就指向ul  
              console.log(this);  // ul
              // e.target 触发了事件的对象 我们点击的是li e.target 指向的就是li
              console.log(e.target);  // li
})
    </script>
  • this 是事件绑定的元素, 这个函数的调用者(绑定这个事件的元素)
  • e.target 是事件触发的元素。



事件委托**

  • 事件委托也称为事件代理,在 jQuery 里面称为事件委派
  • 事件委托的原理


    • 不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点
<body>
    <ul>
        <li>知否知否,点我应有弹框在手!</li>
        <li>知否知否,点我应有弹框在手!</li>
        <li>知否知否,点我应有弹框在手!</li>
        <li>知否知否,点我应有弹框在手!</li>
        <li>知否知否,点我应有弹框在手!</li>
    </ul>
    <script>
        // 事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点
        var ul = document.querySelector('ul');
        ul.addEventListener('click', function(e) {
            // alert('知否知否,点我应有弹框在手!');
            // e.target 这个可以得到我们点击的对象
            e.target.style.backgroundColor = 'pink';
            // 点了谁,就让谁的style里面的backgroundColor颜色变为pink
        })
    </script>
</body>

以上案例:给 ul 注册点击事件,然后利用事件对象的 target 来找到当前点击的 li,因为点击 li,事件会冒泡到 ul 上, ul 有注册事件,就会触发事件监听器。



BOM



DOM



浏览器缓存



cdn缓存



js执行机制

单线程



ES6

ECMAScript 6.0 (2015)是JavaScript语言的下一代

标准

,弥补传统JavaScript语言的缺陷

完全支持ES6特性的浏览器比较少

  • 但大部分浏览器都至少完整支持ES5
  • 随着Node.js技术的出现,Babel等工具的使用可以使得ES6开发的程序可以

    被“编译”为ES5版本的代码

    ,在任意浏览器上被运行,因此使用ES6才被大部分开发人员的接受

严格模式 ‘use strict’

https://developer.mozilla.org/en-US/

https://developer.mozilla.org/zh-CN/docs/learn




对象是一组无序的相关属性和方法的集合

类抽象了对象的公共部分,它泛指某一大类(class)

// 创建类
class name {
    // class body
}

// 创建对象
var XX = new name();
<script>
    // 1. 创建类 class  创建一个 明星类
    class Star {
        // constructor 构造器或者构造函数
        constructor(uname, age) {
            this.uname = uname;
            this.age = age;
        }
    }

    // 2. 利用类创建对象 new
    var ldh = new Star('刘德华', 18);
    var zxy = new Star('张学友', 20);
    console.log(ldh);
    console.log(zxy);
</script>
class Person {
  constructor(name,age) {   
      // constructor 称为构造器或者构造函数
      this.name = name;
      this.age = age;
    }
   say() {
      console.log(this.name + '你好');
   }
}      
var ldh = new Person('刘德华', 18); 
ldh.say() 


注意

: 方法之间不能加逗号分隔,同时方法不需要添加 function 关键字



继承


super

可以调用父类的构造函数,也可以调用父类的普通函数

// 父类
class Father {
    
}
// 子类继承父类
class Son extends Father {
    
}

<script>
    // 父类有加法方法
    class Father {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
        sum() {
            console.log(this.x + this.y);
        }
    }
    // 子类继承父类加法方法 同时 扩展减法方法
    class Son extends Father {
        constructor(x, y) {
            // 利用super 调用父类的构造函数
            // super 必须在子类this之前调用
            super(x, y);
            this.x = x;
            this.y = y;
        }
        subtract() {
            console.log(this.x - this.y);
        }
    }
    var son = new Son(5, 3);
    son.subtract();
    son.sum();
</script>



调用父类的构造函数

// 父类
class Person {
    constructor(surname){
        this.surname = surname;
    }
}
// 子类继承父类
class Student entends Person {
    constructor(surname,firstname) {
        super(surname);					//调用父类的 constructor(surname)
        this.firstname = firstname;		//定义子类独有的属性
    }
}

注意:子类在构造函数中使用super,必须放到this前面(必须先调用父类的构造方法,在使用子类构造方法)

// 父类
class Father {
    constructor(surname){
        this.surname = surname;
    }
    saySurname() {
        console.log('我的姓是' + this.surname);
    }
}
// 子类继承父类
class Son entends Father {
    constructor(surname,firstname) {
        super(surname);					//调用父类的 constructor(surname)
        this.firstname = firstname;		//定义子类独有的属性
    }
    sayFirstname() {
        console.log('我的名字是:' + this.firstname);
    }
}

var damao = new Son('刘','德华');
damao.saySurname();
damao.sayFirstname();



调用父类的普通函数

class Father {
    say() {
        return '我是爸爸';
    }
}
class Son extends Father {
    say(){
        // super.say() super调用父类的方法
        return super.say() + '的儿子';
    }
}

var damao = new Son();
console.log(damao.say());

多个方法之间不需要添加逗号分隔

继承中属性和方法的查找原则:就近原则,先看子类,再看父类



三个注意点

  1. 在ES6中类没有变量提升,所以必须先定义类,才能通过类实例化对象
  2. 类里面的共有属性和方法一定要加

    this

    使用
  3. 类里面的this指向:

    • constructor 里面的

      this

      指向实例对象
    • 方法里面的

      this

      指向这个方法的调用者
<body>
    <button>点击</button>
    <script>
        var that;
        var _that;
        class Star {
            constructor(uname, age) {
                // constructor 里面的this 指向的是 创建的实例对象
                that = this;
                this.uname = uname;
                this.age = age;
                // this.sing();
                this.btn = document.querySelector('button');
                this.btn.onclick = this.sing;
            }
            sing() {
            // 这个sing方法里面的this 指向的是 btn 这个按钮,因为这个按钮调用了这个函数
                console.log(that.uname); 
                // that里面存储的是constructor里面的this
            }
            dance() {
                // 这个dance里面的this 指向的是实例对象 ldh 因为ldh 调用了这个函数
                _that = this;
                console.log(this);
            }
        }
        var ldh = new Star('刘德华');
        console.log(that === ldh);
        ldh.dance();
        console.log(_that === ldh);

        // 1. 在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象

        // 2. 类里面的共有的属性和方法一定要加this使用.
    </script>
</body>



静态成员和实例成员

// 构造函数中的属性和方法我们称为成员,成员可以添加
function Star(uname,age) {
    this.uname = uname;
    this.age = age;
    this.sing = function() {
        console.log('我会唱歌');
    }
}
var ldh = new Star('刘德华',18);

// 实例成员就是构造函数内部通过this添加的成员  uname age sing  就是实例成员
// 实例成员只能通过实例化的对象来访问
ldh.sing();
Star.uname; // undefined     不可以通过构造函数来访问实例成员

// 静态成员就是在构造函数本身上添加的成员 sex 就是静态成员
// 静态成员只能通过构造函数来访问
Star.sex = '男';
Star.sex;
ldh.sex; // undefined  不能通过对象来访问

构造函数方法很好用,但是存在

浪费内存的问题



原型

构造函数通过原型分配的函数是所有对象所共享的,这样就

解决了内存浪费问题


JavaScript 规定,每一个构造函数都有一个prototype属性,指向另一个对象,注意

这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有


我们可以把那些不变的方法,直接定义在prototype 对象上,这样所有对象的实例就可以共享这些方法

  • 一般情况下,我们的公共属性定义到构造函数里面, 公共的方法我们放到原型对象身上

原型是什么?

  • 一个对象,我们也称为

    prototype

    为原型对象

原型的作用是什么?


  • 共享方法



对象原型 __ proto __

对象都会有一个属性

proto

指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype 原型对象的属性和方法,就是因为对象有_proto_原型的存在。

_proto_对象原型和原型对象 prototype 是等价的

_proto_对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype

在这里插入图片描述


  • Star.prototype 和 ldh.

    proto


    指向相同
<body>
    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        Star.prototype.sing = function() {
            console.log('我会唱歌');
        }
        var ldh = new Star('刘德华', 18);
        var zxy = new Star('张学友', 19);
        ldh.sing();
        console.log(ldh); 
		// 对象身上系统自己添加一个 __proto__ 指向我们构造函数的原型对象 prototype
        console.log(ldh.__proto__ === Star.prototype);
        // 方法的查找规则: 首先先看ldh 对象身上是否有 sing 方法,如果有就执行这个对象上的sing
        // 如果没有sing 这个方法,因为有 __proto__ 的存在,就去构造函数原型对象prototype身上去查找sing这个方法
    </script>
</body>



constructor构造函数

对象原型(__ proto __) 和构造函数(prototype)原型对象 里面都有一个属性 constructor 属性, constructor 我们称为构造函数,因为它指回构造函数本身。

constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数

一般情况下,

对象的方法都在构造函数(prototype)的原型对象中设置

如果有多个对象的方法,我们可以给原型对象prototype采取对象形式赋值,但是这样会覆盖构造函数原型对象原来的内容,这样修改后的原型对象constructor就不再指向当前构造函数了。此时,

我们可以在修改后的原型对象中,添加一个constructor指向原来的构造函数

<body>
    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        // 很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数
        // Star.prototype.sing = function() {
        //     console.log('我会唱歌');
        // };
        // Star.prototype.movie = function() {
        //     console.log('我会演电影');
        // }
        Star.prototype = {
            // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
            constructor: Star,
            sing: function() {
                console.log('我会唱歌');
            },
            movie: function() {
                console.log('我会演电影');
            }
        }
        var ldh = new Star('刘德华', 18);
        var zxy = new Star('张学友', 19);
    </script>
</body>



原型链

查找规则

  1. 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
  2. 如果没有就查找它的原型(也就是_proto_指向的prototype原型对象)
  3. 如果还没有就查找原型对象的原型(Object的原型对象)
  4. 依次类推一直找到Object为止(null)
  5. __ proto __对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线

在这里插入图片描述

<body>
    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        Star.prototype.sing = function() {
            console.log('我会唱歌');
        }
        var ldh = new Star('刘德华', 18);
        // 1. 只要是对象就有__proto__ 原型, 指向原型对象
        console.log(Star.prototype);
        console.log(Star.prototype.__proto__ === Object.prototype);
        // 2.我们Star原型对象里面的__proto__原型指向的是 Object.prototype
        console.log(Object.prototype.__proto__);
        // 3. 我们Object.prototype原型对象里面的__proto__原型  指向为 null
    </script>
</body>



原型对象this指向


  • 构造函数中的 this指向我们的实例对象
  • 原型对象里面放的是方法,

    这个方法里面的this指向的是这个方法的调用者

    ,也就是这个实例对象
<body>
    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        var that;
        Star.prototype.sing = function() {
            console.log('我会唱歌');
            that = this;
        }
        var ldh = new Star('刘德华', 18);
        // 1. 在构造函数中,里面this指向的是对象实例 ldh
        ldh.sing();
        console.log(that === ldh);

        // 2.原型对象函数里面的this 指向的是 实例对象 ldh
    </script>

</body>



扩展内置对象

  • 可以通过原型对象,对原来的内置对象进行扩展自定义的方法
  • 比如给数组增加自定义求偶数和的功能
<body>
    <script>
        // 原型对象的应用 扩展内置对象方法

        Array.prototype.sum = function() {
            var sum = 0;
            for (var i = 0; i < this.length; i++) {
                sum += this[i];
            }
            return sum;
        };
 
        var arr = [1, 2, 3];
        console.log(arr.sum());
        console.log(Array.prototype);
        var arr1 = new Array(11, 22, 33);
        console.log(arr1.sum());
    </script>
</body>

注意:数组和字符串内置对象不能给原型对象覆盖操作

Array.prototype = {}

,只能是

Array.prototype.xxx = function(){}

的方式



call()

调用这个函数,并且修改函数运行时的 this 指向

<body>
    <script>
        // call 方法
        function fn(x, y) {
            console.log('我希望我的希望有希望');
            console.log(this);		// Object{...}
            console.log(x + y);		// 3
        }

        var o = {
            name: 'andy'
        };
        // fn();
        // 1. call() 可以调用函数
        // fn.call();
        // 2. call() 可以改变这个函数的this指向 此时这个函数的this 就指向了o这个对象
        fn.call(o, 1, 2);
    </script>
</body>



原型链继承

一般情况下,对象的方法都在构造函数的原型对象中设置,通过构造函数无法继承父类方法

核心原理:

将子类所共享的方法提取出来,让

子类的 prototype 原型对象 = new 父类()


本质: 子类原型对象等于是实例化父类,因为父类实例化之后另外开辟空间,就不会影响原来父类原型对象

将子类的constructor重新指向子类的构造函数

function SuperType() {
   this.property =  true;
}

SuperType.prototype.getSuperValue =  function() {
    return this.property;
};

function SubType() {
	this.subproperty =  false;
}

// 继承SuperType
SubType.prototype =  new SuperType();
SubType.prototype.getSubValue =  function () {  //注意 不能通过对象字面量的方式添加新方法,否则上一行无效
	return this.subproperty;
};

let instance = new SubType();
 console.log(instance.getSuperValue());  // true

特点:

  1. 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
  2. 父类新增原型方法/原型属性,子类都能访问到
  3. 简单,易于实现

缺点:

  1. 要想为子类新增属性和方法,必须要在

    new SuperType()

    这样的语句之后执行,不能放到构造器中
  2. 无法实现多继承
  3. 来自原型对象的所有属性被所有实例共享(来自原型对象的引用属性是所有实例共享的)(详细请看附录代码:)
  4. 创建子类实例时,无法向父类构造函数传参



构造函数继承

  • 核心原理: 通过

    call()

    把父类型的 this 指向子类型的 this,这样就可以实现子类型继承父类型的属性
<body>
    <script>
        // 借用父构造函数继承属性
        // 1. 父构造函数
        function Father(uname, age) {
            // this 指向父构造函数的对象实例
            this.uname = uname;
            this.age = age;
        }
        // 2 .子构造函数 
        function Son(uname, age, score) {
            // this 指向子构造函数的对象实例
            Father.call(this, uname, age);
            this.score = score;
        }
        var son = new Son('刘德华', 18, 100);
        console.log(son);
    </script>
</body>

特点:

  1. 子类实例

    共享父类引用属性

    的问题
  2. 创建子类实例时,

    可以向父类传递参数
  3. 可以实现

    多继承

    (call多个父类对象)

缺点:

  1. 实例并不是父类的实例,只是子类的实例
  2. 只能继承父类的实例属性和方法,不能继承原型属性/方法
  3. 无法实现函数复用,

    每个子类都有父类实例函数的副本,影响性能



组合继承

function Parent(name) {
  this.name = name;
  this.newArr = ["red", "blue", "green"];
}
Parent.prototype.sayName = function() {
  console.log(this.name);
};

function Child(name) {
  Parent.call(this, name);
  this.age = 26;
}

Child.prototype = new Parent();
//重写Child.prototype的constructor属性,使其执行自己的构造函数Child
Child.prototype.constructor = Child;
Child.prototype.sayAge = function() {
  console.log(this.age);
};

var Person1 = new Child("heyushuo");
console.log(Person1);
Person1.newArr.push("yellow");
console.log(Person.newArr); //["red", "blue", "green","yellow"]
Person.sayName(); //heyushuo

var Person2 = new Child("kebi");
console.log(Person2.newArr); //["red", "blue", "green"]
Person.sayName(); //kebi

重点:

结合了两种模式的优点,

传参和复用

特点:

  1. 可以继承父类原型上的属性,可以传参,可复用。
  2. 每个新实例引入的构造函数属性是私有的。

缺点:


调用了两次父类构造函数(耗内存)

,创建的实例和原型上存在两份相同的属性即(name 和 newArr);



原型式继承

不自定义类型的情况下,临时创建一个构造函数,借助已有的对象作为临时构造函数的原型,然后在此基础实例化对象,并返回。本质上是object()对传入其中的对象执行了一次浅复制。

function object(o){
 function F(){}
 F.prototype = o;
 return new F();
} 

var person = {
 name: "Nicholas",
 friends: ["Shelby", "Court", "Van"]
};

var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");

alert(person.friends); //"Shelby,Court,Van,Rob,Barbie" 

复制代码

重点:

用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。==object.create()==就是这个原理

特点:

类似于复制一个对象,用函数来包装

缺点:

  1. 所有实例都会继承原型上的属性
  2. 无法实现复用(新实例属性都是后面添加的)
  3. 效率较低,内存占用高(因为

    要拷贝父类的属性



寄生式继承

与原型式继承比较接近的一种继承方式是寄生式继承,类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。寄生式继承同样适合主要关注对象,而不在乎类型和构造函数的场景

function object(person) {
 function F() {}
 F.prototype = person
 return new F()
}
function createAnother(original){
	let clone = object(original); // 通过调用函数创建一个新对象
	clone.sayHi = function() { // 以某种方式增强这个对象
	console.log("hi");
};
	return clone; // 返回这个对象
}

复制代码

重点:

就是给原型式继承外面套了个壳子。

优点:

没有创建自定义类型,因为只是套了个壳子返回对象(这个),这个函数顺理成章就成了创建的新对象。

缺点:

没用到原型,无法复用。



寄生组合式继承(常用)

寄生:在函数内返回对象然后调用

组合:1、函数的原型等于另一个实例。2、在函数中用apply或者call引入另一个构造函数,可传参

最常用的继承方式,也是最佳的,组合继承会调用两次父类构造函数,存在效率问题。其实本质上子类原型最终是要包含父类对象的所有实例属性,子类构造函数只要在执行时重写自己的原型就行了。基本思路是不通过调用父类构造函数给子类原型赋值,而是取得父类原型的一个副本。说到底就是使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型。

//核心代码
function object(person) {
 function F(params) {}
 F.prototype = person
 return new F()
}
function inheritPrototype(SubType,SuperType) {
 let prototype = object(SuperType.prototype) //生成一个父类原型的副本

 //重写这个实例的constructor
 prototype.constructor = SubType

 //将这个对象副本赋值给 子类的原型
 SubType.prototype = prototype
}

function SuperType(name) {
	this.name = name;
	this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function() {
	console.log(this.name);
};
function SubType(name, age) {
	SuperType.call(this, name);
	this.age = age;
}

//调用inheritPrototype函数给子类原型赋值,修复了组合继承的问题
inheritPrototype(SubType, SuperType);

SubType.prototype.sayAge = function() {
	console.log(this.age);
};

复制代码

重点:修复了组合继承的问题



类的本质

  1. class 本质还是 function
  2. 类的所有方法都定义在类的

    prototype

    属性上
  3. 类创建的实例,里面也有

    _proto_

    指向类的

    prototype

    原型对象



let



var问题

var a = []
for(var i = 0; i < 3; i++) {
	a[i] = function() {
		console.log(i)
	}
}
a[0]()
a[1]()
a[2]()
// 都是输出3

// 解决方案一:使用let
var a = []
for(let i = 0; i < 3; i++) {
	a[i] = function() {
		console.log(i)
	}
}
a[0]()
a[1]()
a[2]()
// 输出 0 1 2 

// 解决方案二:使用闭包
var a = []
for(var i = 0; i < 3; i++) {
    (function (i) {
        a[i] = function() {
            console.log(i)
        }
    }
    )(i)  // 这里的i是 var i= 传进入的,然后这个做完之后 再i++,
}
a[0]()
a[1]()
a[2]()

// 闭包的原理
var name = 'why'
function abc(name) {
    console.log(name)
}
name = 'who'
abc('aa') // 此时在调用abc之前,不管name是否被修改了,都不会影响abc('aa')的结果

// ---------------
var不受块作用域的限制,哪里都能用



let无变量提升

	
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>let</title>
</head>
<body>
  <script>
  document.write(v);
  var v = 3;
  document.write(v);
  </script>
</body>
</html>

对比

	
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>let</title>
</head>
<body>
  <script>
  document.write(l);
  let l = 3;
  document.write(l);
  </script>
</body>
</html>



作用域

特定块级作用域有效

  • window ,a

  • 块:i

  • 脚本:v ,t

意思就是 var的相当于全局,除了变量提升外,下面的i只能在特定位置即for里面。v和t只能在当前脚本内生效

var a = 1
const v = 1
let t = 1
for(let i = 0; i < 10; i ++) {
  console.log(i)
}
var a = []
for(var i = 0; i < 10; i ++) {
    a[i] = function() {
    console.log(i)
    }
}
a[6]()  // 输出10,循环之后 var取值为最后一次赋值

 var a = []
 for(let i = 0; i < 10; i ++) {
     a[i] = function() {
         console.log(i)
     }
 }
a[6]() // 输出6


// 报错, 超出作用域
const arr = [1, 2, 3, 4]
const num = 0
for(let i = 0; i< arr.length; i ++) {
    if(arr[i] == num) {
        break;
    }
}
console.log(i)

//
const arr = [1, 2, 3, 4]
const num = 0
for(var i = 0; i< arr.length; i ++) {
    if(arr[i] == num) {
        break;
    }
}
console.log(i) //  4



暂时性死区

// 暂时性死区, 全局变量到局部变量之间 是不可以用变量的
let tmp = 3
if (true) {
    tmp = 4  // 报错
    let tmp = 123
    console.log(tmp)
}



不可重复声明



const

const声明一个只读的常量:一旦声明,常量的值就不能改变


对象或数组

的内容可以改变,但是

引用

不能改变

const a = {name: "张三"}
a.name = "lisi"
console.log(a.name) // lisi,可以改变值
        
const a = 2
let b = a // 新的空间
b = 4
console.log(a)  // 2

const一旦声明变量,就

必须初始化

,作用域和let一样,不存在变量提申,同样存在暂时性死区



解构赋值 ***

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)



数组

let [a, b, c] = [1, 2, 3]
console.log(a + " " + b + " " + c ) //  1 2 3 

let [a, b, c] = [1]
console.log(a + " " + b + " " + c ) //  1 undefined undefined,解构赋值不成功,就等于undefined



对象

名字必须相对应,才能取到正确的值

let {foo, bar:zzz} = {foo: 'aaa', bar: "bbb"};
console.log(foo, zzz)
// 左边的zzz表示解构赋值之后的变量名称改成zzz
const node = {
    loc: {
        start: {line: 1,column: 5}
    }};
let { loc: { start: { line }} } = node;

对象的解构赋值,将现有

对象的方法

,赋值到某个变量

const { log } = console
log('hello') // hello



字符串

const [a, b, c, d, e] = 'hello';
// a = h, b = e, c = l, d = l, e = o

let {length:len} = 'hello'; 
console.log(len) // 5


let {toString:s1} = 123;
let {toString:s2} = true;
console.log(typeof(s1)) // 返回的是一个函数
console.log((s1)) 

等号右边的 不是对象时,就将其转为

对象



交换数据

let x = 1;
let y = 2;
[x,y] = [y,x]
console.log(x, y) // 2, 1



函数

  • 返回多个值
function e() {
  return [1, 2, 3]
}
let [a, b, b] = e();
  • 传入多个值
function fun([x, y, z]) {
  console.log(x, y, z)
}
fun([1, 2, 3]) // 不常用

function fun({x, y, z}) {
  console.log(x, y, z)
}
fun({x:1, y:2, z:3})// 用这个



字符串长度

使用[]包起来,因为出现unicode编码的时候,

console.log([..."x\uD83D\uDE80y"].length) // 3



字符串扩展



遍历

for(let c of "hello") {
    console.log(c)
}



模板字符串

使用反引号,可当作普通字符串使用,或者在字符串中

嵌入变量或者任何JavaScript表达式

如果在模板字符串中需要使用反引号,则前面要用反斜杠转义

let str = `hello`
console.log(typeof(str)) // string

let str2 = `hell
o`;
console.log(str2) // 保留了 换行


function add(x, y) {
    return x + y
}
let name = "zhangsan"
let age = 19
let str = `hello, I'm ${name}, I'm ${age}, ${add(age,2)}`;
console.log(str) // 插入变量,调用函数


// 生成html
 window.onload = function() {
     const json = {
         username: "zhangsan",
         registered: 5,
         vip: false,
     }

     let html = `
        <ul>
        <li>用户名:${json.username}</li>
        <li>注册念想${json.registered}</li>    
        <li>用户名:${json.vip ? "是" : "false"}</li>    
        </ul>
        `
     const div = document.getElementById("user-info")
     div.innerHTML = html
 }

// 方式二
function getUserInfo() {
    const json = {
        username: "zhangsan",
        registered: 5,
        vip: false,
    }
    let html = `
    <ul>
    <li>用户名:${json.username}</li>
    <li>注册念想${json.registered}</li>    
    <li>用户名:${json.vip ? "是" : "false"}</li>    
    </ul>
    `
    const div = document.getElementById("user-info")
    div.innerHTML = html
    // return undefined,不写返回值默认是返回undefined的
}
window.onload = getUserInfo
// 当页面的 body加载完成之后
// 函数 没有括号,相当于将getUserInfo给 onload事件,没有返回值的函数赋值的时候不需要加上括号



字符串函数

和java类似

includes(substr)表示是否找到了参数字符串
startsWith(substr)表示参数字符串是否在原字符串的头部
endsWith(substr)表示参数字符串是否在原字符串的尾部
matchAll()方法返回一个正则表达式在当前字符串的所有匹配

str3.trimEnd();
str3.trimStart();

const str = "aaa"
console.log(str.repeat(0)) // 空字符串
console.log(str.repeat(2.3)) // 向下取整  2

let str2 = "abbbb"
str2 = str2.replace("b", "c")
// str2 = str2.replaceAll("b", "c")
console.log(str2)



函数扩展



参数默认值

function fetch(url, {method="GET", data={}}) {
	console.log(method, data);
}
fetch("/api/userinfo", {method:"POST"})
fetch("/api/userinfo", {}) // ******
fetch("/api/userinfo") // 会报错

// 改进
function fetch(url, {method="GET", data={}} = {}) {
     console.log(method);
     console.log(data);
 }
fetch("/api/userinfo") 



// JSON -> string,string -> JSON
function fetch(url, {method="GET", data={}}) {
    console.log(method);
    let str = JSON.stringify(data); // JSON -> string
    let json = JSON.parse(str) // string -> JSON
    console.log(json.id);
}
fetch("/api/userinfo", {method:"POST", data:{id:3}})



区别

function m1({x = 0, y = 0} = {}) {
    console.log(x, y)
}
function m2({x, y} = {x : 0, y : 0}) {
    console.log(x, y)
}
m1({x: 3}) // 3 0
m2({x: 3}) // 3 undefined
m1({a: 1}) // 0 0
m2({b: 1}) // 不对应,解构赋值不成功则返回   undefined undefined



强制要有参数

只有

传入undefined 或者 不传入参数

才会报错

function throwIfMissing() {
    throw new Error("Missing parameter")
}
function add(a = throwIfMissing()) {
    console.log(a)
}
// add()  // 异常
// add(undefined) // 异常
add(2)
add(null)
add(false)



函数属性

function第一次赋值给了谁 ,那么name就是谁

let f = function() {
}
let g = f
console.log(g.name) // f
console.log(g.length) // 0 参数个数



rest参数

只要能用of遍历的,都可以用 …rest展开

function add(a, b, rest) {

    if(Array.isArray(rest) && rest.length > 0) {
        console.log(rest)
    } 
}
add(1, 2, [3, 4]) // 要传数组

function add(a, b, ...rest) {
    if(Array.isArray(rest) && rest.length > 0) {
    console.log(rest)
    } 
}
add(1, 2, 3, 4)
console.log(add.length) // 2



惰性求值

// 惰性求值
function add(a, b = x + 1) {
	console.log(b)
}
let x = 99
add(5) // 100



拼接URL

// 方式一
url 必须传,不传递 报错
method 默认get,
不传为{} ,data,json对象
get的 将参数拼接

        function throwIfMissing() {
            throw new Error("Missing parameter")
        }
        function m(url = throwIfMissing(), {method="GET", data={}} = {}) {
            if(method === "GET") {
                let str = JSON.stringify(data)
                str = str.replace("{","?")
                str = str.replaceAll("\"", "")
                str = str.replaceAll(":", "=")
                str = str.replaceAll(",","&")
                str = str.replace("}","")
                console.log(url + str)
            } else if(method === "POST") {
                console.log(url)
            }
        }
        m("http://localhost:8080/index", {data:{id:1,id2:2}});
        m("http://localhost:8080/index");

        m("http://localhost:8080/index", {method:"POST"});


// 方式二
function request(url = throwIfMissing(), {method="GET", data={}} = {}) {
    if(method.toUpperCase() === "GET") {
        let params = ""
        let index = 0
        for(let key in data) {
            let value = data[key]
            params += `${(index > 0 ? "&" : "")}${key}=${value}`
            index ++;
        }

        if(params !== "") {
            url +="?" + params
        }
    }
    console.log(url);
}
request("/fdsf",{data:{id:1,id2:2}})



箭头函数

简单例子:

const add = (
const add3 = (a, b) => {
    let result = a + b;
    if(result > 10) {
        result = 10
    }
    return result;
}
console.log(add3(5, 9))

// 返回值
const add4 = (a, b) => Math.min(a + b, 10)

// 加了大括号没写返回值相当于 return undefined
const add4 = (a, b) => {Math.min(a + b, 10)}
// 等同于
const add4 = (a, b) => {Math.min(a + b, 10);return undefined}

// 返回对象,需要加上大括号
const add5 = (a, b) => ({name: "张三"})

// 无返回值
const add6 = (a, b) => void a + b


map filter reduce


非 in place

方法

let a = [1,2,3,4]

// 过滤
let b = a.filter((e) => e % 2 == 1)

// 映射
console.log(a.filter((e) => e % 2 == 1).map((e) => e ** 2)) // 1 9

// 累加
console.log(a.filter((e) => e % 2 == 1).reduce((acc,e) => acc + e)) 


箭头函数的注意点

  1. 函数体内的this对象,就是

    定义时所在的对象

    ,即定义该函数时所在的作用域指向的对象
  2. 不可当作构造函数,即

    不可以使用new命令
  3. 不可用arguments对象,该对象在函数体内不存在。如果要用,可用rest 参数代替
  4. 不可用yield命令,因此箭头函数不能用作 Generator 函数
function bar() {
setTimeout(() => console.log(this), 1000)
}
bar()


var A = {
    name: "A",
    say: () => {
        console.log(this.name)
        // 这里的this是window,输出 window
    }
}
A.

箭头函数的this 就是往外一层找,看时候有this,一直往外找 直到找到



数组扩展



遍历

  • currentValue : 数组当前项的值
  • index: 数组当前项的索引
  • arr: 数组对象本身
<body>
    <script>
        // forEach 迭代(遍历) 数组
        var arr = [1, 2, 3];
        var sum = 0;
        arr.forEach(function(value, index, array) {
            console.log('每个数组元素' + value);
            console.log('每个数组元素的索引号' + index);
            console.log('数组本身' + array);
            sum += value;
        })
        console.log(sum);
    </script>
</body>



过滤

<body>
    <script>
        // filter 筛选数组
        var arr = [12, 66, 4, 88, 3, 7];
        var newArr = arr.filter(function(value, index) {
            // return value >= 20;
            return value % 2 === 0;
        });
        console.log(newArr);
    </script>
</body>



some

<body>
    <script>
        // some 查找数组中是否有满足条件的元素 
        var arr1 = ['red', 'pink', 'blue'];
        var flag1 = arr1.some(function(value) {
            return value == 'pink';
        });
        console.log(flag1);
        // 1. filter 也是查找满足条件的元素 返回的是一个数组 而且是把所有满足条件的元素返回回来
        // 2. some 也是查找满足条件的元素是否存在  返回的是一个布尔值 如果查找到第一个满足条件的元素就终止循环
    </script>
</body>



数组索引

//返回数组元素索引号方法 indexOf(数组元素)  作用就是返回该数组元素的索引号
//它只发返回第一个满足条件的索引号
//如果找不到元素,则返回-1
var arr = ['red','green','blue','pink','blue'];
console.log(arr.indexOf('blue'));  // 2

console.log(arr.lastIndexOf('blue')); // 4
<body>
    <script>
        // some 查找数组中是否有满足条件的元素 
        var arr1 = ['red', 'pink', 'blue'];
        var flag1 = arr1.some(function(value) {
            return value == 'pink';
        });
        console.log(flag1);
        // 1. filter 也是查找满足条件的元素 返回的是一个数组 而且是把所有满足条件的元素返回回来
        // 2. some 也是查找满足条件的元素是否存在  返回的是一个布尔值 如果查找到第一个满足条件的元素就终止循环
    </script>
</body>



数组转字符串

// 1.toString() 将我们的数组转换为字符串
var arr = [1, 2, 3];
console.log(arr.toString()); // 1,2,3
// 2.join('分隔符')
var arr1 = ['green', 'blue', 'red'];
console.log(arr1.join()); // 不写默认用逗号分割
console.log(arr1.join('-')); //  green-blue-red
console.log(arr1.join('&')); // green&blue&red



复制数组

// 1.
let a = [1, 2, 3]
let b = [...a]

// 2.
let [...a2] = a;



合并数组

let arr = [1, 2, 3]
let arr2 = [1, 2, 3]	
let arr3 = [1, 2, 3]
let newArr = [...arr, ...arr2, ...arr3]
console.log(newArr) // 1 2 3 1 2 3 1 2 3 



与解构赋值结合

const [first, ...rest] = [1, 2, 3, 4, 5];
// first: 1
// rest: [2, 3, 4, 5]

const [first, ...rest] = [];
// first: undefined
// rest: []

const [first, ...rest] = ["foo"];
// first: "foo"
// rest: []



列表变为数组

function fun(...nums) {
	console.log(Array.isArray(nums));
}
fun(1, 2, 3) // true



其他方法

方法名 说明 返回值
concat() 连接两个或多个数组 不影响原数组 返回一个新的数组
slice() 数组截取slice(begin,end) 返回被截取项目的新数组
splice() 数组删除splice(第几个开始要删除的个数) 返回被删除项目的新数组,这个会影响原数组



对象方法

<body>
    <script>
        // 用于获取对象自身所有的属性
        var obj = {
            id: 1,
            pname: '小米',
            price: 1999,
            num: 2000
        };
        var arr = Object.keys(obj);
        console.log(arr);
        arr.forEach(function(value) {
            console.log(value);
            // id
            // pname
            // price
            // num
        })
    </script>
</body>



Object.defineProperty()

  • obj : 目标对象
  • prop : 需定义或修改的属性的名字
  • descriptor : 目标属性所拥有的特性
<body>
    <script>
        // Object.defineProperty() 定义新属性或修改原有的属性
        var obj = {
            id: 1,
            pname: '小米',
            price: 1999
        };
        // 1. 以前的对象添加和修改属性的方式
        // obj.num = 1000;
        // obj.price = 99;
        // console.log(obj);
        // 2. Object.defineProperty() 定义新属性或修改原有的属性
        Object.defineProperty(obj, 'num', {
            value: 1000,
            enumerable: true
        });
        console.log(obj);
        Object.defineProperty(obj, 'price', {
            value: 9.9
        });
        console.log(obj);
        Object.defineProperty(obj, 'id', {
            // 如果值为false 不允许修改这个属性值 默认值也是false
            writable: false,
        });
        obj.id = 2;
        console.log(obj);
        Object.defineProperty(obj, 'address', {
            value: '中国山东蓝翔技校xx单元',
            // 如果只为false 不允许修改这个属性值 默认值也是false
            writable: false,
            // enumerable 如果值为false 则不允许遍历, 默认的值是 false
            enumerable: false,
            // configurable 如果为false 则不允许删除这个属性 不允许在修改第三个参数里面的特性 默认为false
            configurable: false
        });
        console.log(obj);
        console.log(Object.keys(obj));
        delete obj.address;
        console.log(obj);
        delete obj.pname;
        console.log(obj);
        Object.defineProperty(obj, 'address', {
            value: '中国山东蓝翔技校xx单元',
            // 如果值为false 不允许修改这个属性值 默认值也是false
            writable: true,
            // enumerable 如果值为false 则不允许遍历, 默认的值是 false
            enumerable: true,
            // configurable 如果为false 则不允许删除这个属性 默认为false
            configurable: true
        });
        console.log(obj.address);
    </script>
</body>

第三个参数 descriptor 说明:以对象形式{ }书写

value:设置属性的值,默认为undefined

writeable: 值是否可以重写 true | false 默认为false

enumerable: 目标属性是否可以被枚举 true | false 默认为false

configurable: 目标属性是否可以被删除或是否可以再次修改特性 true | false 默认为false



函数进阶**



Set

用的少

操作

  • add(value):添加某个值,返回 Set 结构本身
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功
  • has(value):返回一个布尔值,表示该值是否为Set的成员
  • clear():清除所有成员,没有返回值

遍历

  • keys() 返回键名的遍历器
  • values() 返回键值的遍历器
  • entries() 返回键值对的遍历器
  • forEach() 使用回调函数遍历每个成员
const s = new Set()

let a = [2,2,2,3,3,3,4]
for(let i of a) {
s.add(i)
}
console.log(s) // 2 3 4 
console.log(s.delete(5)) // false
console.log(s.has(2)) // true 哈希查找
console.log(s.clear())

// 遍历
for(let v of s.keys()) {
    console.log(v)
}
// 遍历
let entries = s.entries()
for(let k of entries) {
    console.log(k)
}



Map

  • 键可以为任意类型

  • 只有对同一个对象的引用,Map 结构才将其视为同一个键。Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键

  • set(key, value)

  • get(key)

  • has(key)

  • delete(key)

  • clear()

const m1 = new Map()
m1.set("name", "张三")
console.log(m1)
console.log(m1.get("name"))


// key只能是String
// 如果不是String,会自动转成String
console.log("****")
const m = new Map()
let o = {}
o["name"] = "lisi"
console.log(o) // {name: "lisi"}
console.log(o["name"]); // lisi

m.set(o, "fdsfsd")
console.log(m) // {Object => "fdsfsd"}
console.log(m.get(o)); // fdsfsd
console.log("******")

let p = {id:3} 
o[p] = "zhangsan"
console.log(o[p]); // zhangsan
console.log(o); // {name: "lisi", [object Object]: "zhangsan"}

let p1 = {id:4}
o[p1] = "lisi"
console.log(o); // {name: "lisi", [object Object]: "lisi"}
console.log(o[p1]); // lisi


遍历 和set一样



map转为数组

 const myMap = new Map()
    .set(true, 7)
    .set({foo: 3}, ['abc']);
  [...myMap]
  // [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]



数组转为map

new Map([
    [true, 7],
    [{foo: 3}, ['abc']]
  ])
  // Map {
  //   true => 7,
  //   Object {foo: 3} => ['abc']
  // }



map转为对象

如果所有 Map 的键都是字符串,它可以无损地转为对象。

  function strMapToObj(strMap) {
    let obj = Object.create(null);
    for (let [k,v] of strMap) {
      obj[k] = v;
    }
    return obj;
  }
 
  const myMap = new Map()
    .set('yes', true)
    .set('no', false);
  strMapToObj(myMap)
  // { yes: true, no: false }
  • 如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。



对象转为map

对象转为 Map 可以通过Object.entries()。

  let obj = {"a":1, "b":2};
  let map = new Map(Object.entries(obj));

此外,也可以自己实现一个转换函数。

  function objToStrMap(obj) {
    let strMap = new Map();
    for (let k of Object.keys(obj)) {
      strMap.set(k, obj[k]);
    }
    return strMap;
  }
 
  objToStrMap({yes: true, no: false})
  // Map {"yes" => true, "no" => false}



map转为JSON

一种情况是,Map 的键名都是字符串,这时可以选择转为对象 JSON。

  function strMapToJson(strMap) {
    return JSON.stringify(strMapToObj(strMap));
  }
 
  let myMap = new Map().set('yes', true).set('no', false);
  strMapToJson(myMap)
  // '{"yes":true,"no":false}'

另一种情况是,Map 的键名有非字符串,这时可以选择转为数组 JSON。

  function mapToArrayJson(map) {
    return JSON.stringify([...map]);
  }
 
  let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
  mapToArrayJson(myMap)
  // '[[true,7],[{"foo":3},["abc"]]]'



json转为map

JSON 转为 Map,正常情况下,所有键名都是字符串。

  function jsonToStrMap(jsonStr) {
    return objToStrMap(JSON.parse(jsonStr));
  }
 
  jsonToStrMap('{"yes": true, "no": false}')
  // Map {'yes' => true, 'no' => false}

但是,有一种特殊情况,整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为 Map。这往往是 Map 转为数组 JSON 的逆操作。

  function jsonToMap(jsonStr) {
    return new Map(JSON.parse(jsonStr));
  }
 
  jsonToMap('[[true,7],[{"foo":3},["abc"]]]')
  // Map {true => 7, Object {foo: 3} => ['abc']}



Promise



异步编程

, promise是对异步函数的封装

在JavaScript的世界中,所有的代码都是

单线程

执行的

  • 由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行

  • 例如,我们有一个函数test,它接受一个参数,然后判断这个参数是否小于1。

  • 但不幸的是,它作出这个判断需要消耗很长的时间,浏览器不可能等待这个结果。

      if(test(3)) {
        // do something
      } else {
        // do otherthing
      }
    

    因此,JavaScript使用了异步的概念,使用回调来处理返回结果:

      test(3, testSuccess, testFail);
      function testSuccess(){
      }
      function testFail(){
      }
    // 写法还有很多
    

不够直观,而且可能存在

回调地狱

,而Promise可以优雅的解决这些问题

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,

使得控制异步操作更加容易

var p = Promise(test);
  p.then(testSuccess).catch(testFail);
  function testSuccess(){
  }
  
  function testFail(){
  }

// 术语 
success:resolve
fail:reject

Promise对象代表

一个异步操作

  • pending(进行中)
  • fulfilled(已成功)
  • rejected(已失败)

Promise对象的状态改变,只有两种可能

  • 从pending变为fulfilled
  • 从pending变为rejected



基本使用

	
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Promise</title>
</head>
<body>
  <div id="test-promise-log"></div>
  <script>
    let logging = document.getElementById('test-promise-log');
 
    // 输出log到页面:
    function log(s) {
      let p = document.createElement('p');
      p.innerHTML = s;
      logging.appendChild(p);
    }
  
// 创建promise的时候 里面的异步操作就已经开始执行了
    let p = new Promise(function (resolve, reject) {
      let timeOut = Math.random() * 2;
      setTimeout(function () {
        if (timeOut < 1) {
          resolve('200 OK');
        }
        else {
          reject('timeout in ' + timeOut + ' seconds.');
        }
      }, timeOut * 1000);
    });
    log('about to test promise');
    
    p.then(function (r) {
      log('Done: ' + r);
    }).catch(function (reason) {
      log('Failed: ' + reason);
    });
  </script>
</body>
</html>



回调地狱

<script>
    new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log("hello world")
        console.log("hello world")
        console.log("hello world")
        console.log("hello world")
        console.log("hello world")
        setTimeout(() => {
          console.log("hello world")
          console.log("hello world")
          console.log("hello world")
          console.log("hello world")
          console.log("hello world")
          setTimeout( () => {
            console.log("hello world")
            console.log("hello world")
            console.log("hello world")
            console.log("hello world")
            console.log("hello world")
          }, 1000)
        }, 1000)
      }, 1000)
    })
  </script>

解决方案

<script>
    new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve()
      }, 1000)
    }).then(() => {
        console.log("hello world")
        console.log("hello world")
        console.log("hello world")
        console.log("hello world")
        console.log("hello world")
        
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve()
          }, 1000)
        }).then(() => {
          console.log("hello world")
          console.log("hello world")
          console.log("hello world")
          console.log("hello world")
          console.log("hello world")

          return new Promise((resolve, reject) => {
            setTimeout(() => {
              resolve()
            }, 1000)
          }).then(() => {
            console.log("hello world")
            console.log("hello world")
            console.log("hello world")
            console.log("hello world")
            console.log("hello world")
          })

        })
    })
  </script>



promise缺点

  • 无法取消Promise,一旦新建它就会立即执行,无法中途取消
  • 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部



all方法

两个异步请求都返回结果时,再进行下一步操作

  <script>
    // 可迭代对象:比如数组
    Promise.all([
      new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('p1')
        }, 1000)
      }),
      new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('p2')
        }, 3000)
      })
    ]).then(results => {
      console.log(results[0])
      console.log(results[1])
    })
  </script>

ajax XmlHttpRequest

不能跨域



async

为什么要使用

async

当然

Promise

也不是最终完美的异步解决方案,它的最大问题还是在于写法太违背人思维

在人们长期的同步函数调用的经验下,我们更喜欢同步函数调用的方式:

  let result = test();
  if(result) {
  } else {
  }
// 有没有可能test是一个异步函数,但我们不用异步函数,而用同步函数的写法?
// 所以使用 async await

async是Promise的语法糖 ,使异步代码易于编写和阅读

  • async:申明一个 异步的function
  • await:等待一个 异步任务执行完成的的结果
 async function test() {
   throw new Error("error") // async方法抛异常的时候是 reject, catch
 }
test().then(function(data){
    console.log(data)
}).catch(function(err){
    console.log(err)
})

async function test() {
    return "hello"; // async函数返回一个值的时候,Promise的resolve方法会负责传递这个值,then
}
test().then(function(data){
    console.log(data)
}).catch(function(err){
    console.log(err)
})



await

函数执行时一旦遇到await就会先返回,等到这个结果才会继续往下执行


await

只能放在

async

函数内部使用

async function test() {
  return "hello"
}

async function test1() {
    let res = await test()
    console.log(res + "11")
    return res	
}
test1().then(function(res){
  console.log(res)
})



fetch

GET请求

成功

async function request() {
  let data = await fetch("http://192.168.136.95:8181/phone/index")
  return data.json()
}

async function requestJson() {
  let json = await request()
  console.log(json)
}
requestJson() // 如果requestJson没有返回值,那么调用的时候不用加上thenl

失败

async function request() {
  let data = await fetch("http://192.168.136.95:8181/phone/index")
  throw new Error("error")
  return data.json()
}

async function requestJson() {
    try {
      let json = await request()
      console.log(json)
    } catch(e) {
      console.log(e)
    }
}
requestJson()



模块

  • 使用模块就是通过命名空间对各类业务对象进行一定的封装,

    防止命名冲突
  • 在ES6之前,JavaScript本身没有模块支持,但社区创造了令人印象深刻的解决方案。两个最重要的(也是不相容的)标准是:

    AMD



    CommonJS
  • ES6 module 结合了CommonJS和AMD的优点:类似CommonJS,具有简洁的语法,对

    循环依赖的支持

    ;类似AMD,支持

    异步加载和有条件的模块加载
  • 简单来说:ES6 module 使用 export 导出模块的内容,并使用 import 导入模块的内容

以前,解决命名冲突

// 1.js 小明
(function() {
	let flag = true
})()

// 3.js 小明
if(flag) {
	console.log("aaa")
}

// 2.js 小红
(function() {
    let flag = true
})()

// 0.js
<script src='./1.js'> </script>
<script src='./3.js'> </script>
<script src='./2.js'> </script>
// 虽然可以解决但是小明自己写的文件3.js就用不了1.js的flag了

// 自己实现模块化
// 此时module1 的结果为 true,就可以给3.js使用了
// 1.js
var module1 = (function() {
	let flag = true
    return flag
})()
// 3.js
if(module1.flag) {
	console.log("aaa")
}

现在使用模块化就好了AMD,CommonJS



export

  • Named exports(命名导出;每个模块可有

    多个

  • Default exports(默认导出;每个模块只能

    一个

可以导出(对外提供)模块的内容:

函数



对象



变量



命名导出
// 1)声明时导出
export var myVar1 = 'a';
export let myVar2 = 'b';
export const MY_CONST = 'c';
export function myFunc() {}

// 2)声明后导出
var myVar3 = 'a';
export { myVar3 };

// 3)别名导出
var myVar4 = 'a';
export { myVar4 as myVar };


默认导出
// 1)声明时导出
export default function() {}

// 2)别名设置为default导出
export default function name1() {}
export { name1 as default };



使用模块文件

在支持ES6模块的浏览器中,引入ES6 module 的JS文件时,使用属性 type=“module”

// 0.js
<script type="module" src="./1.js"></script>
<script type="module" src="./2.js"></script>
// 这样就不会有命名冲突了,但是1.js中不能直接用2.js中的东西,想要使用的话,2.js需要导出对应的东西



import



导入默认
 // math.js
  export default function cube(x) {
      return x * x * x;
  }
   
  // app.js:导入默认导出的模块时,需要指定模块名称
  import cube from './math.js';
  console.log(cube(3)); // => 27


导入命名

import axios from ‘axios’; 从 node_modelus下导入

  // math.js
  export function add(a, b) {
      return a + b;
  }
   
  // app.js:指定使用math模块的add命名导出
  import { add } from './math.js';
  console.log(add(1, 2)); // => 3

  // 导入所有的命名导出作为math对象的成员
  import * as math from './math.js';
  console.log(math.add(1, 2)); // => 3
// math.js
export function add(a, b) {
    return a + b;
}
// 立即执行
(function() {
    console.log('hello math.js');
})();

// app.js
import { add } from './math.js'; // => hello math.js



Node.js



node.js是什么

http://nodejs.cn/api/

http://nodejs.cn/learn

  • Node.js 是一个开源与跨平台的

    JavaScript 运行时环境
  • Node.js 在浏览器外运行

    V8 JavaScript 引擎(Google Chrome 的内核)

    ,性能非常好
  • 可以写

    客户端



    服务端

    代码
  • 大量的库,npm 开源包自由使用



与浏览器区别

  • JavaScript 发展的速度非常快,但是浏览器发展得慢一些,并且用户的升级速度也慢一些,因此有时在 web 上,不得不使用较旧的 JavaScript / ECMAScript 版本

    可用

    Babel 将代码转换为与 ES5 兼容的代码,再交付给浏览器

    , Node.js 不需要这样做

  • Node.js 用

    CommonJS 模块系统

    ,浏览器用

    ES 模块标准

    在 Node.js 中使用

    require()

    ,要用import,则

    package.js要加上 ,“type”: “module”

    , 而在浏览器中则使用

    import

  • 在浏览器中,大多做的是 DOM 或其他 Web 平台 API(例如 Cookies)进行交互。在 Node.js 中是不存在的。 没有

    document



    window

    、所有其他的对象



安装

linux 安装nodejs 使用nvm安装,

nvm

是一种流行的运行 Node.js 的方式。 例如,它可以轻松地

切换 Node.js 版本

,也可以安装新版本用以尝试并且当出现问题时轻松地

回滚

。这对于使用旧版本的 Node.js 来测试代码非常有用

windows中,用chocolatey安装,或者直接下载安装包

  • 选择

    LTS长期支持版本,稳定版
node -v 
npm -v
都显示版本,则安装成功



配置淘宝源

win + x的 powershell下

npm config set registry "https://registry.npm.taobao.org"



运行JS

// 1.js
function hello() {
    console.log("hello world")
}
hello()

// cmd , cd 进入js目录
node 1.js



node.js程序退出

当在控制台中运行程序时,可以使用

ctrl-C

将其关闭

编程的方式退出 Node.js 程序:

process.exit()

http://nodejs.cn/learn/how-to-exit-from-a-nodejs-program



Vscode

npm i jshint -g
npm i eslint -g

vscode创建.jshintrc

{
    "esversion": 9,// 自动路由 需要11
    "asi": true // 没写分号不提示报错
}



vue.js 脚手架

// 安装
npm i @vue/cli -g

// 查看是否成功
vue -V // 大写的V



模块

Node.js以模块来组织项目结构,项目中可以

引入其他模块的功能

  • require

    • CommonJS标准
    • 民间标准
    • NodeJS官方使用
    • 动态载入
    • 通过赋值完成符号载入
  • import

    • ES6标准
    • 通用
    • 未来希望
    • 静态载入,

      ES11

      之后也支持动态载入
    • 通过解构来完成符号载入

使用import和require导入模块时以如下顺序查找模块


  1. 内置模块

    • 传递给 require 函数的是 NodeJS 内置模块名称,不做路径解析,返回

      内部模块的导出对象
    • 例如require(‘fs’)

  2. node_modules目录

    • node_modules 是Node.js中用于存放模块的目录
    • 可以分为全局node_modules目录和项目局部node_modules目录
    • 例如某个模块的绝对路径是/home/user/hello.js,
    • 在该模块中使用 require(‘foo/bar’) 方式加载模块时
    • NodeJS 依次尝试使用以下路径:

      • /home/user/node_modules/foo/bar
      • /home/node_modules/foo/bar
      • /node_modules/foo/bar
  3. NODE_PATH环境变量

    • 与 PATH 环境变量类似,NodeJS 允许通过 NODE_PATH 环境变量来指定额外的模块搜索路径。
    • 例如定义了以下 NODE_PATH 环境变量:

      • NODE_PATH=/home/user/lib:/home/lib
      • 当使用 require(‘foo/bar’)的方式加载模块时,则 NodeJS 依次尝试以下路径。

        • /home/user/lib/foo/bar
        • /home/lib/foo/bar



入口文件

一个Node.js项目一般以

main.js

作为入口文件

项目中可以会包含多个不同的模块

  • 模块可以 以.js文件组织
  • 也可以 以不同的文件夹组织(更常见)
  • 例如:
/home/user/lib/
           cat/
              head.js
              body.js
              main.js
              util/
                index.js

以文件组织的模块,直接导入文件即可

var body = require('./body');
// 或
import body from "./body"

以目录组织的模块,若模块中的文件名为index.js,那么只需要导入到目录级别即可

var util = require('./util');
// 或
import util from "./util"

注意:

@表示 src目录



包描述文件

如果想自定义入口模块的文件名和存放位置,就需要在包目录下包含一个 package.json 文件,并在其中指定入口模块的路径

例如:

 - /home/user/lib/
      - cat/
          + doc/
          - lib/
              head.js
              body.js
              main.js
          + tests/
          package.json

其中package.json内容为

  {
      "name": "cat",
      "main": "./lib/main.js"
      // node.js会根据package.json下的main找到入口模块所在的位置
  }



包描述文件扩展

package.json除了可以对

项目进行描述

之外,还有

对其他模块的依赖

创建package.json

  1. 手动创建
  2. 或者通过

    npm init

    命令生成规范的package.json文件(

    推荐

  • name是项目
  • main是 入口文件
  • script是 node 脚本
  • browserslist 浏览器说明
  • … 其他参考官方API
"browserslist": [
  "> 1%",
  "last 2 versions",
  "not ie <= 8"
]


此配置意味着需要支持使用率超过 1%,浏览器的最新的 2 个主版本,但不含 IE8 及更低的版本

指定依赖包:

  1. dependencies:在生产环境中需要依赖的包

    通过npm install -S 或者 –save
  2. devDependencies:在开发和测试
  3. 通过npm install -D

package.lock.json 描述

包的版本



npm

npm是

node.js安装包管理工具

,能解决 NodeJS 代码部署上的很多问题

常见的使用场景有以下几种:

  • 允许用户从 NPM 服务器下载别人编写的三方包到本地使用
  • 允许用户从 NPM 服务器下载并安装别人编写的命令行程序到本地使用
  • 允许用户将自己编写的包或命令行程序上传到 NPM 服务器供别人使用



包版本

npm list
npm list -g
npm list 软件包的名称



安装

npm i 包

一般是安装仅有本项目使用的一些库文件

将安装包放在 ./node_modules 下(运行 npm 命令时所在的目录),如果没有 node_modules 目录,会在当前执行 npm 命令的目录下生成 node_modules 目录

npm i 包 -g
  • 一般用于安装需要全局运行的工具

  • 将安装包放在 /usr/local 下或者你 node 的安装目录

  • 可以直接在命令行里任意目录中使用。这是使用全局安装的主要原因

  • 可以使用下面的命令来查看全局的包安装的位置:

    npm prefix -g
    
  • 添加依赖

      npm install <包> --save
      # 或
      npm i <包> -S
      # dependencies,项目打包时,需要发布到生产环境的
    
  • 添加开发依赖

      npm install <包> --save-dev
      # 或
      npm i <包> -D
      # devDependencies,开发 测试
    
//全局安装信息
npm ls -g

//列出当前项目中的包
npm ls



卸载 更新

// 卸载
npm uninstall 包名

// 更新
npm update 包名

// 更新所有包 
npm update



初始化项目

cd 进入js目录

npm init

在工程目录下

npm i qs -S

安装less

npm i less less-loader -D

将less编译为css

lessc style/style.less style/style.css



复制项目

进入copy目录,只要有package-lock.json,和package.json。npm i即可安装依赖 ,可以将开发者所依赖的包全部下载重现



导入qs

package.json

{
  "name": "first_node",
  "version": "1.0.0",
  "description": "a demo project",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Chuang",
  "license": "ISC",
  "type": "module", // 加上这个
  "dependencies": {
    "qs": "^6.10.1"
  },
  "devDependencies": {
    "less": "^4.1.1",
    "less-loader": "^10.0.1"
  }
}

import qs from 'qs'; 

let data = {id: 3, page: 1, pageCount: 10, name: "a&b c"}
let params = qs.stringify(data)
console.log(params)
// id=3&page=1&pageCount=10&name=a%26b%20c

let recovery = qs.parse(params)
console.log(recovery)
// { id: '3', page: '1', pageCount: '10', name: 'a&b c' }



npm脚本

package.json文件里面定义脚本命令

{
    // ...
    "scripts": {
      "build": "node build.js"
    }
  }

这些定义在package.json里面的脚本,就称为

npm脚本

npm run build
// 等同于执行
node build.js


npm脚本的优势

  • 目的相关脚本,可以集中在一个地方

  • npm run

    可以查看所有可调用的脚本名
  • 不同项目的脚本命令,只要功能相同,就可以有同样的对外接口。用户不需要知道怎么测试你的项目,只要运行

    npm run test

    即可
  • 可以利用 npm 提供的很多辅助功能

    src=”./1.js”>

// 这样就不会有命名冲突了,但是1.js中不能直接用2.js中的东西,想要使用的话,2.js需要导出对应的东西




#### import

##### 导入默认

```javascript
 // math.js
  export default function cube(x) {
      return x * x * x;
  }
   
  // app.js:导入默认导出的模块时,需要指定模块名称
  import cube from './math.js';
  console.log(cube(3)); // => 27


导入命名

import axios from ‘axios’; 从 node_modelus下导入

  // math.js
  export function add(a, b) {
      return a + b;
  }
   
  // app.js:指定使用math模块的add命名导出
  import { add } from './math.js';
  console.log(add(1, 2)); // => 3

  // 导入所有的命名导出作为math对象的成员
  import * as math from './math.js';
  console.log(math.add(1, 2)); // => 3
// math.js
export function add(a, b) {
    return a + b;
}
// 立即执行
(function() {
    console.log('hello math.js');
})();

// app.js
import { add } from './math.js'; // => hello math.js



Node.js



node.js是什么

http://nodejs.cn/api/

http://nodejs.cn/learn

  • Node.js 是一个开源与跨平台的

    JavaScript 运行时环境
  • Node.js 在浏览器外运行

    V8 JavaScript 引擎(Google Chrome 的内核)

    ,性能非常好
  • 可以写

    客户端



    服务端

    代码
  • 大量的库,npm 开源包自由使用



与浏览器区别

  • JavaScript 发展的速度非常快,但是浏览器发展得慢一些,并且用户的升级速度也慢一些,因此有时在 web 上,不得不使用较旧的 JavaScript / ECMAScript 版本

    可用

    Babel 将代码转换为与 ES5 兼容的代码,再交付给浏览器

    , Node.js 不需要这样做

  • Node.js 用

    CommonJS 模块系统

    ,浏览器用

    ES 模块标准

    在 Node.js 中使用

    require()

    ,要用import,则

    package.js要加上 ,“type”: “module”

    , 而在浏览器中则使用

    import

  • 在浏览器中,大多做的是 DOM 或其他 Web 平台 API(例如 Cookies)进行交互。在 Node.js 中是不存在的。 没有

    document



    window

    、所有其他的对象



安装

linux 安装nodejs 使用nvm安装,

nvm

是一种流行的运行 Node.js 的方式。 例如,它可以轻松地

切换 Node.js 版本

,也可以安装新版本用以尝试并且当出现问题时轻松地

回滚

。这对于使用旧版本的 Node.js 来测试代码非常有用

windows中,用chocolatey安装,或者直接下载安装包

  • 选择

    LTS长期支持版本,稳定版
node -v 
npm -v
都显示版本,则安装成功



配置淘宝源

win + x的 powershell下

npm config set registry "https://registry.npm.taobao.org"



运行JS

// 1.js
function hello() {
    console.log("hello world")
}
hello()

// cmd , cd 进入js目录
node 1.js



node.js程序退出

当在控制台中运行程序时,可以使用

ctrl-C

将其关闭

编程的方式退出 Node.js 程序:

process.exit()

http://nodejs.cn/learn/how-to-exit-from-a-nodejs-program



Vscode

npm i jshint -g
npm i eslint -g

vscode创建.jshintrc

{
    "esversion": 9,// 自动路由 需要11
    "asi": true // 没写分号不提示报错
}



vue.js 脚手架

// 安装
npm i @vue/cli -g

// 查看是否成功
vue -V // 大写的V



模块

Node.js以模块来组织项目结构,项目中可以

引入其他模块的功能

  • require

    • CommonJS标准
    • 民间标准
    • NodeJS官方使用
    • 动态载入
    • 通过赋值完成符号载入
  • import

    • ES6标准
    • 通用
    • 未来希望
    • 静态载入,

      ES11

      之后也支持动态载入
    • 通过解构来完成符号载入

使用import和require导入模块时以如下顺序查找模块


  1. 内置模块

    • 传递给 require 函数的是 NodeJS 内置模块名称,不做路径解析,返回

      内部模块的导出对象
    • 例如require(‘fs’)

  2. node_modules目录

    • node_modules 是Node.js中用于存放模块的目录
    • 可以分为全局node_modules目录和项目局部node_modules目录
    • 例如某个模块的绝对路径是/home/user/hello.js,
    • 在该模块中使用 require(‘foo/bar’) 方式加载模块时
    • NodeJS 依次尝试使用以下路径:

      • /home/user/node_modules/foo/bar
      • /home/node_modules/foo/bar
      • /node_modules/foo/bar
  3. NODE_PATH环境变量

    • 与 PATH 环境变量类似,NodeJS 允许通过 NODE_PATH 环境变量来指定额外的模块搜索路径。
    • 例如定义了以下 NODE_PATH 环境变量:

      • NODE_PATH=/home/user/lib:/home/lib
      • 当使用 require(‘foo/bar’)的方式加载模块时,则 NodeJS 依次尝试以下路径。

        • /home/user/lib/foo/bar
        • /home/lib/foo/bar



入口文件

一个Node.js项目一般以

main.js

作为入口文件

项目中可以会包含多个不同的模块

  • 模块可以 以.js文件组织
  • 也可以 以不同的文件夹组织(更常见)
  • 例如:
/home/user/lib/
           cat/
              head.js
              body.js
              main.js
              util/
                index.js

以文件组织的模块,直接导入文件即可

var body = require('./body');
// 或
import body from "./body"

以目录组织的模块,若模块中的文件名为index.js,那么只需要导入到目录级别即可

var util = require('./util');
// 或
import util from "./util"

注意:

@表示 src目录



包描述文件

如果想自定义入口模块的文件名和存放位置,就需要在包目录下包含一个 package.json 文件,并在其中指定入口模块的路径

例如:

 - /home/user/lib/
      - cat/
          + doc/
          - lib/
              head.js
              body.js
              main.js
          + tests/
          package.json

其中package.json内容为

  {
      "name": "cat",
      "main": "./lib/main.js"
      // node.js会根据package.json下的main找到入口模块所在的位置
  }



包描述文件扩展

package.json除了可以对

项目进行描述

之外,还有

对其他模块的依赖

创建package.json

  1. 手动创建
  2. 或者通过

    npm init

    命令生成规范的package.json文件(

    推荐

  • name是项目
  • main是 入口文件
  • script是 node 脚本
  • browserslist 浏览器说明
  • … 其他参考官方API
"browserslist": [
  "> 1%",
  "last 2 versions",
  "not ie <= 8"
]


此配置意味着需要支持使用率超过 1%,浏览器的最新的 2 个主版本,但不含 IE8 及更低的版本

指定依赖包:

  1. dependencies:在生产环境中需要依赖的包

    通过npm install -S 或者 –save
  2. devDependencies:在开发和测试
  3. 通过npm install -D

package.lock.json 描述

包的版本



npm

npm是

node.js安装包管理工具

,能解决 NodeJS 代码部署上的很多问题

常见的使用场景有以下几种:

  • 允许用户从 NPM 服务器下载别人编写的三方包到本地使用
  • 允许用户从 NPM 服务器下载并安装别人编写的命令行程序到本地使用
  • 允许用户将自己编写的包或命令行程序上传到 NPM 服务器供别人使用



包版本

npm list
npm list -g
npm list 软件包的名称



安装

npm i 包

一般是安装仅有本项目使用的一些库文件

将安装包放在 ./node_modules 下(运行 npm 命令时所在的目录),如果没有 node_modules 目录,会在当前执行 npm 命令的目录下生成 node_modules 目录

npm i 包 -g
  • 一般用于安装需要全局运行的工具

  • 将安装包放在 /usr/local 下或者你 node 的安装目录

  • 可以直接在命令行里任意目录中使用。这是使用全局安装的主要原因

  • 可以使用下面的命令来查看全局的包安装的位置:

    npm prefix -g
    
  • 添加依赖

      npm install <包> --save
      # 或
      npm i <包> -S
      # dependencies,项目打包时,需要发布到生产环境的
    
  • 添加开发依赖

      npm install <包> --save-dev
      # 或
      npm i <包> -D
      # devDependencies,开发 测试
    
//全局安装信息
npm ls -g

//列出当前项目中的包
npm ls



卸载 更新

// 卸载
npm uninstall 包名

// 更新
npm update 包名

// 更新所有包 
npm update



初始化项目

cd 进入js目录

npm init

在工程目录下

npm i qs -S

安装less

npm i less less-loader -D

将less编译为css

lessc style/style.less style/style.css



复制项目

进入copy目录,只要有package-lock.json,和package.json。npm i即可安装依赖 ,可以将开发者所依赖的包全部下载重现



导入qs

package.json

{
  "name": "first_node",
  "version": "1.0.0",
  "description": "a demo project",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Chuang",
  "license": "ISC",
  "type": "module", // 加上这个
  "dependencies": {
    "qs": "^6.10.1"
  },
  "devDependencies": {
    "less": "^4.1.1",
    "less-loader": "^10.0.1"
  }
}

import qs from 'qs'; 

let data = {id: 3, page: 1, pageCount: 10, name: "a&b c"}
let params = qs.stringify(data)
console.log(params)
// id=3&page=1&pageCount=10&name=a%26b%20c

let recovery = qs.parse(params)
console.log(recovery)
// { id: '3', page: '1', pageCount: '10', name: 'a&b c' }



npm脚本

package.json文件里面定义脚本命令

{
    // ...
    "scripts": {
      "build": "node build.js"
    }
  }

这些定义在package.json里面的脚本,就称为

npm脚本

npm run build
// 等同于执行
node build.js


npm脚本的优势

  • 目的相关脚本,可以集中在一个地方

  • npm run

    可以查看所有可调用的脚本名
  • 不同项目的脚本命令,只要功能相同,就可以有同样的对外接口。用户不需要知道怎么测试你的项目,只要运行

    npm run test

    即可
  • 可以利用 npm 提供的很多辅助功能



版权声明:本文为huang6chen6原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。