js

  • 基本数据类型(6)
    • null
    • undefined
    • boolean
    • number
  • 复杂数据类型:object
    • 对象创建
    • 继承
    • 操作
      • 定义属性
      • 读写属性
      • 删除属性
      • 合并对象
  • 判断类型
    • typeof
    • instanceof
    • Object.prototype.toString.call()
    • 隐式转换
    • 判断两个对象是否相等
  • Arrary
    • 方法
  • var、let、const
    • var
    • let
    • const
  • 变量提升、函数提升、提升优先级
    • 变量提升
    • 函数提升
    • 函数变量同名
    • 隐式全局变量不提升
  • 函数
    • 构造函数
      • 原型链
      • new过程
    • 嵌套函数
    • 闭包
    • 回调函数
    • 箭头函数
    • 综合案例
  • this
  • 事件循环
    • 任务队列
      • 异步
      • Promise
      • SetTimeout
      • 例子
  • ajax

注释 单行 //内容 多行 /*内容*/

基本数据类型(6)

null、undefined、number、string、boolean、symbol(ES6后新增)

通过字面量的方式创建:var a = ‘string’;,这时它就是基本类型值;通过构造函数的方式创建:var a = new String(‘string’);这时它是对象类型。
基本类型是没有属性和方法的,但仍然可以使用对象才有的属性方法。这时因为在对基本类型使用属性方法的时候,后台会隐式的创建这个基本类型的对象,之后再销毁这个对象

null

undefined

boolean

数据类型 if判断为true if判断为false
string 任何非空字符串,空格也算 ‘’(空字符串
number 任何非0数字、Infinity 0、NaN
object 任何对象 null
undefined 不适用 undefined

null==undefined、null==null、undefined==undefined都为true

number

1.浮点数、整数

Number()
parseInt()
parseFloat()

2.NaN

1、表示不是数字,但是其实它是一个特殊的数字(NaN:Not a Number)
2、当运算操作符错误的时候,一般会得到NaN
3、NaN具有传染性,即NaN参与任何运算,结果都为NaN
4、NaN与任何数值都不相等,NaN == NaN :falseisNaN()
用来检测当前这个值是否是有效数字,如果不是有效数字,检测的结果是true;反之为false
isNaN(0)->false
isNaN(NaN)->true
isNaN(‘12’)–>false 当我们使用isNaN检测值的时候,检测的值不是number类型的,浏览器会默认的把值转化为number类型,然后再去检测

3.Infinity

1、Infinity:数据超过了JS可以表示的范围,是一个特殊的数字
2、Infinity与其他数据类型进行操作运算,结果还是Infinity
3、数字除以0得到Infinity
4、Infinity+或*Infinity为Infinity,Infinity-或/或%Infinity为NaNisFinite()
如果是NaN或者Infinity返回false,否则返回true
isFinite('12')->true

复杂数据类型:object

引用类型可细分为:Object、Array、Date、Function、RegExp

对象创建

1.直接json创建
var student = {name: "Tom",age: 22,sex: "boy";setName: function () { }
}
2.new+ Object
var student = new Object();
student.name = "Tom";
student.age = 22;
student.sex = "boy";
student.setName = function () { }
这两种方法同一接口创建多个对象会产生大量重复代码
3.函数创建(工厂模式)
特点:大规模创建同类实例
缺点:无法区分该对象的类型,分别存放浪费内存
function createStudent(name, age, sex) {var s = new Object();s.name = name;s.age = age;s.sex = sex;s.setName = function () { }return s;
}
var s1 = createStudent("Jack", 20, "boy");
var s2 = createStudent("Tom", 22, "boy");
console.log(s1, s2);
console.log(s1 instanceof createStudent);//false
console.log(s1 instanceof Object);//true
4.new+ 构造函数
特点:识别对象
缺点:浪费内存
function Student(name, age, sex) {this.name = name;this.age = age;this.sex = sex;this.setName = function () { }
}
var s1 = new Student("Jack", 20, "boy");
var s2 = new Student("Tom", 22, "boy");
console.log(s1 instanceof Object);//true
console.log(s1 instanceof Student);//true
5.原型模式
特点:所有实例对象共享原型对象的属性方法,并可以覆盖同名属性方法,实现私有
function Student(name, age, sex) {Student.prototype.name = "Tom";Student.prototype.age = 20;Student.prototype.sex = "boy";Student.prototype.setName = function () { }
}
var s1 = new Student();
var s2 = new Student();
s1.name = "Jack";
console.log(s1, s2);
6.构造函数原型模式组合
function Student(name, age, sex) {this.name = name;this.age = age;this.sex = sex;
}
Student.prototype = {constructor=Student,setName=function () { }
}
var s1 = new Student("Jack", 20, "boy");

继承

【继承】是直接有的,【访问】是走原型链的。

1.原型链继承:子类原型为父类的一个实例对象
特点:
实例是子类的实例,也是父类的实例
父类新增原型方法 / 原型属性,子类都能【访问】到
简单,易于实现
缺点:
无法实现多继承
来自原型对象的所有属性被所有实例共享
创建子类实例时,无法向父类构造函数传参
要想为子类新增原型属性和方法,必须要在Student.prototype = new Person() 之后执行,不能放到
构造器中,实例属性方法可以放在构造器//父类型
function Person(name, age) {this.name = name;this.age = age;this.play = [1, 2, 3];this.setName = function () { }
}
Person.prototype.setAge = function () { }
//子类型
function Student(price) {this.price = price;this.setScore = function () { }
}
//子类型的原型为父类型的一个实例对象
Student.prototype = new Person("Visky",28);//不传参也可以,我只是为了显著分别才传参
//因为会改变原型的指向,所以应该放到重新指定之后
Student.prototype.sex = "boy";//Person实例对象Visky增加sex="boy"
Student.prototype.setsex = function () { }//Person实例对象Visky增加setsex=function(){}
var s1 = new Student(100);//Student通过原型链访问Person函数实例对象
var s2 = new Student(90);//Student通过原型链访问Person函数实例对象
var p1 = new Person("Lily",16);//Person不同的实例对象Lily
console.log(p1.sex);//undefined
console.log(s1.name, s2.name);//Visky Visky
s1.play.push(4);//s1没有play属性,修改的是原型链,所以都能读取
console.log(s1.play, s2.play);//都[1,2,3,4]
console.log(s1 instanceof Person); // true
console.log(s1 instanceof Student); // true
2.借用构造函数继承:子类构造函数通过call()调用父类型构造函数
特点:
解决了原型链继承中子类实例共享父类属性的问题
创建子类实例时,可以向父类传递参数
可以实现多继承(call多个父类对象)
缺点:
实例并不是父类的实例,只是子类的实例
只能继承父类的实例属性和方法,不能访问原型属性和方法
无法实现函数复用,每个子类都有父类实例函数的副本,影响性能function Person(name, age) {this.name = name;this.age = age;this.play = [1, 2, 3];this.setAge = function () {console.log("setAge") }
}
Person.prototype.setName = function () { }
function Student(name, age, price) {Person.call(this, name, age);//每个子类都有不同的父类实例对象//相当于:this.Person(name,age);this.price = price;this.setScore = function () { }
}
var s1 = new Student("Tom", 20, 100);
var s2 = new Student("Jerry", 18, 90);
console.log(s1.name, s2.name);//Tom Jerry
s1.setName();//Uncaught TypeError: s1.setName is not a function
s1.setAge();//setAge
s1.play.push(4);
console.log(s1.play, s2.play);//[1, 2, 3, 4] [1, 2, 3]
console.log(s1 instanceof Person); // false
console.log(s1 instanceof Student); // true
3.原型链 + 借用构造函数组合继承
特点:
可以继承实例属性 / 方法,也可以访问原型属性 / 方法
不存在属性共享问题
可传参
函数可复用
缺点:
调用了两次父类构造函数,生成了两份实例function Person(name, age) {this.name = name;this.age = age;this.play = [1, 2, 3]this.setName = function () { }
}
Person.prototype.setAge = function () {console.log("setAge")}
function Student(name, age, price) {Person.call(this, name, age);//继承实例方法属性this.price = price;this.setScore = function () { }
}
Student.prototype = new Person();//访问原型方法属性
Student.prototype.constructor = Student;//修复构造函数指向
Student.prototype.sayHello = function () {console.log("Hello")}
var s1 = new Student("Tom", 20, 100);
var s2 = new Student("Jerrry", 18, 90);
console.log(s1.name, s2.name);//Tom Jerrry
s1.sayHello();//Hello
s1.setAge();//setAge
s1.play.push(4);
console.log(s1.play, s2.play);//[1,2,3,4] [1,2,3]
console.log(s1 instanceof Person); // true
console.log(s1 instanceof Student); // true
console.log(s1.constructor) //Student函数
3.1组成继承优化1:子类原型对象指向父类原型对象
特点:
不会初始化两次实例方法 / 属性,避免的组合继承的缺点
缺点:
没办法辨别是实例是子类还是父类创造的,子类和父类的构造函数指向是同一个function Person(name, age) {this.name = name;this.age = age;this.play = [1, 2, 3]this.setName = function () { }
}
Person.prototype.setAge = function () {console.log("setAge")}
function Student(name, age, price) {Person.call(this, name, age);//继承实例方法属性this.price = price;this.setScore = function () { }
}
Student.prototype = Person.prototype;//优化点,不需修正构造函数指向
Student.prototype.sayHello = function () {console.log("Hello")}
var p1 = new Person("Peter", 22);
var s1 = new Student("Tom", 20, 100);
var s2 = new Student("Jerrry", 18, 90);
console.log(p1.name, s1.name, s2.name);
p1.setAge();//setAge
p1.sayHello();//Hello
s1.setAge();//setAge
s1.sayHello();//Hello
s1.play.push(4);
console.log(p1.play, s1.play, s2.play);//[1,2,3] [1,2,3,4] [1,2,3]
console.log(p1 instanceof Person, p1 instanceof Student);//true,true父类实例来自子类判断为真
console.log(s1 instanceof Person); // true
console.log(s1 instanceof Student); // true
console.log(s1.constructor) //Person函数
3.2组合继承优化2:原型可以基于已有的对象来创建对象,var B = Object.create(A)以A对象为原型,生成了B对象。B继承了A的所有属性和方法。
function Person(name, age) {this.name = name;this.age = age;this.play = [1, 2, 3]this.setName = function () { }
}
Person.prototype.setAge = function () { }
function Student(name, age, price) {Person.call(this, name, age);//继承实例方法属性this.price = price;this.setScore = function () { }
}
Student.prototype = Object.create(Person.prototype);//优化点,Student继承Person的实例属性函数,可以访问原型属性函数
Student.prototype.constructor = Student;//修正构造函数指向
Student.prototype.satHello = function () { }
var p1 = new Person("Peter", 22);
p1.__proto__.money=1000;
var s1 = new Student("Tom", 20, 100);
var s2 = new Student("Jerrry", 18, 90);
console.log(p1.money, s1.money, s2.money);//1000 1000 1000
s1.play.push(4);
console.log(s1.play, s2.play);//无属性共享
console.log(p1 instanceof Person, p1 instanceof Student);//true,false
console.log(s1 instanceof Person); // true
console.log(s1 instanceof Student); // true
console.log(s1.constructor) //Student

操作

定义属性

1.创建时直接定义

const obj={age:18,
name:'tom'
}

2.点语法添加

const obj={}
obj.age=18
obj.name='tom'

3.Object.defineProperty(obj, prop, descriptor)
configurable
当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
enumerable
当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。默认为 false。

const object1 = {};Object.defineProperty(object1, 'property1', {value: 42,writable: false
});object1.property1 = 77;
// throws an error in strict modeconsole.log(object1.property1);
// expected output: 42

读写属性

1.点语法

var obj = {  //定义对象x : 1
}
console.log(obj.x);  //访问对象属性x,返回1
obj.x = 2;  //重写属性值
console.log(obj.x);  //访问对象属性x,返回2

2.中括号语法
在中括号语法中,必须以字符串形式指定属性名,不能使用标识符。
中括号内可以使用字符串,也可以使用字符型表达式,即只要表达式的值为字符串即可。

obj.x1=4
console.log(obj["x"+1];  //4
console.log(obj["x"]);  //2
obj["x"] = 3;  //重写属性值
console.log(obj["x"]);  //3

3.for in

obj={x:4,y:5,z:6}
for(let k in obj){console.log(obj[k])
}
//4 5 6

4.Object.getOwnPropertyNames

obj={x:4,y:5,z:6}
console.log(Object.getOwnPropertyNames(obj))
//["x", "y", "z"]

5.Object.keys和Object.values

obj={x:4,y:5,z:6}
Object.keys(obj)
//["x", "y", "z"]
Object.values(obj)
//[4, 5, 6]

删除属性

const obj = {x : 1};  //定义对象
delete obj.x;  //删除对象的属性x
console.log(obj.x);  //返回undefined

合并对象

1.Object.assign()

const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { a: 3 };const obj = Object.assign(o1, o2);
console.log(obj); // { a: 1, b: 2}
console.log(o1);  // { a: 1, b: 2}, 注意目标对象自身也会改变。
console.info(o2); // { b: 2 }
const obj1 = Object.assign(o1, o3);
console.log(obj1);// { a: 3, b: 2}
console.log(o1);  // { a: 3, b: 2}
console.info(o3); // { a: 3 }

2.for in

const o1 = {a: 1, d: 4}
const o2 = {b: 2, a: 3}
function assignObj(o1,o2){for(let k in o2){if(o2.hasOwnProperty(k)){if(!o1.hasOwnProperty(k)){o1[k]=o2[k]
}
else{o1[k]=[o1[k],o2[k]]
}
}
}
}
assignObj(o1,o2)
console.log(o1) //{a: [1, 3], d: 4, b: 2}

判断类型

typeof

typeof运算符的返回类型为字符串

返回值 类型
‘boolean’ 布尔类型的变量或值
‘undefined’ 未定义的变量或值
‘string’ 字符串类型的变量或值
‘number’ 数字类型的变量或值
‘object’ 对象类型的变量或值,或者null
‘function’ 函数类型的变量或值

instanceof

检测构造函数的 prototype 属性是否出现在某个实例对象的原型链

var a={};
a instanceof Object  //true
a instanceof Array     //false
var b=[];
b instanceof Array  //true
b instanceof Object //true

Object.prototype.toString.call()

Object.prototype.toString.call(123)
//"[object Number]"Object.prototype.toString.call('str')
//"[object String]"Object.prototype.toString.call(true)
//"[object Boolean]"Object.prototype.toString.call({})
//"[object Object]"Object.prototype.toString.call([])
//"[object Array]"
Object.prototype.toString.call(null)
//"[object Null]"

隐式转换

==

数字==字符串,字符串转为数字比较
布尔值==?,都转为数字
对象==数字/字符串,先尝试valueOf()再尝试toString()转为基本值
1=='2' //false
1=='1' //true
1=='a' //false
true=='a' //false
true=='1' //true
true==1 //true
true==[1] //true
true==[0] //false
[1]==1 //true
[1]=='1' //true

[]

[]转为String是""
[]转为Number是0
[]转为Boolean是true

判断两个对象是否相等

规则:
NaN 和 NaN 相等
[1] 和 [1] 相等
{value:1} 和 {value:1} 相等
1 和 new Number(1) 相等
‘Curly’ 和 new String(‘Curly’) 相等
true 和 new Boolean(‘true’) 相等

Arrary

方法

  • 不改变原数组
    concat() :
    slice() :
    map() :
  • 改变原数组
    push() :
    pop() :
    shift() :
    unshifit() :
    sort() :
    splice() :
    reverse() :

var、let、const

var

如果使用关键字 var 声明一个变量,那么这个变量就属于当前的函数作用域,如果声明是发生在任何函数外的顶层声明,那么这个变量就属于全局作用域
var a=1
function fun(){var a=2
console.log(a)
}
fun() //2
console.log(a) //1如果在声明变量时,省略 var 的话,该变量就会变成全局变量,如全局作用域中存在该变量,就会更新其值
function fun(){a=2
}
fun()
console.log(a) //2var a=1
function a(){a=2
}
fun()
console.log(a) //2var的变量复制,复制的 基本类型值 相互独立,栈内存会开辟一个空间保存新的变量和变量值,所以改变新变量的值对旧变量没有影响
var a=1
var b=a
b=2
console.log(a) //1
复制的 引用类型值 直接用=号赋值属于浅拷贝,只会拷贝对象的指针,实际指向的是堆内存中的同一object,所以改变新值时旧值会同步改变(改变任何一个变量的值,另外一个变量的值也会发生改变)
var a=[1,2]
var b=a
a.push(3) //b.push(3)
console.log(a,b) //[1,2,3] [1,2,3]var a=[1,2]
var b=a
a=[3,4] //重新赋值,不是修改
console.log(a,b) //[3,4] [1,2]

let

let声明的变量具有块作用域的特征,由{}包括起来,if语句和for语句里面的{}也属于块级作用域
for(let i=0;i<5;i++){setTimeout(function(){console.log(i);
},0)
} //0 1 2 3 4
对比var
for(var i=0;i<5;i++){setTimeout(function(){console.log(i);
},0)
} //5 5 5 5 5在同一个块级作用域,不能重复声明变量。
let i=1
function fun(){let i=2
let i=3 //Uncaught SyntaxError: Identifier 'i' has already been declared
}let i=1
function fun(){let i=2
console.log(i)
}
fun() //2
console.log(i) //1let声明的变量不存在变量提升,换一种说法,就是 let 声明存在暂时性死区(TDZ)。
let a=1
console.log(a) //1
console.log(b) //Uncaught ReferenceError: b is not defined
let b=2let复制同var

const

const声明基本数据类型:值保存在变量指向的那个内存地址,因此等同于常量,而且const声明的变量必须经过初始化
const a=1
a=2 //Uncaught TypeError: Assignment to constant variable
const b //Uncaught SyntaxError: Missing initializer in const declaration复合类型const变量保存的是引用。因为复合类型的常量不指向数据,而是指向数据所在的地址,所以通过const声明的复合类型只能保证其地址引用不变,但不能保证其数据不变
arr指向的地址不变
const arr=[]
arr.push(1)
arr[2]=3
console.log(arr) //[1, empty, 3][1,2]和[3,4]的地址不同
const arr=[1,2]
arr=[3,4] //Uncaught SyntaxError: Identifier 'arr' has already been declaredconst变量不会提升
consple.log(a) //Uncaught ReferenceError: consple is not defined
const a=10const和let作用域一致
const i=1
function fun(){const i=2
console.log(i)
}
fun() //2
console.log(i) //1

变量提升、函数提升、提升优先级

在JS执行前会对代码进行预处理,把当前作用域的变量和函数提升到顶部 (全局作用域、局部作用域)。变量只提升声明,赋值依旧在实际代码所在处;函数是整个函数都提升,且函数提升优先级高于变量提升,函数的提升后的位置是在变量提升后的位置之后的,注意:只有声明的变量和函数才会进行提升,隐式全局变量不会提升

变量提升

console.log(a) //undefined,如果没有定义会报错 a is not defined
var a=10
console.log(a) //10

由于在js中代码执行的顺序是从上而下,所以在第一次打印的时候,要找变量a,如果没有变量提升的话应该会报错说a没有定义,而这里输出的是undefined,说明a已经定义了,但是没有赋值。那么这就是变量提升起了作用了。

函数提升

var a=10
fun() //10
function fun(){console.log(a)
}

函数变量同名

console.log(a) //ƒ a(){}
var a =10
function a(){}
console.log(a) //10js解析:
var a
function a(){}
console.log(a)
a=10
console.log(a)
function foo() {console.log(a); //a(){}var a = 1;console.log(a); //1function a() {}console.log(a); //1console.log(b); //b(){}var b = 2;console.log(b); //2function b() {}console.log(b); //2
}
foo()js解析:
function foo() {var a;var b;function a() {}function b() {}console.log(a); //a(){}a = 1;console.log(a); //1console.log(a); //1console.log(b); //b(){}b = 2;console.log(b); //2console.log(b); //2
}
foo();

隐式全局变量不提升

function foo() {console.log(a);console.log(b); // 报错未定义b = 'aaa';var a = 'bbb';console.log(a);console.log(b);
}
foo();

函数

1.参数

ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面
function log(x, y = 'World') {console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
log() //undefined "World"function Point(x = 0, y = 0) {this.x = x;this.y = y;
}
const p = new Point();
p // { x: 0, y: 0 }参数变量是默认声明的,所以函数代码块内不能用let或const再次声明。使用参数默认值时,函数不能有同名参数
function foo(x){let x=1; // errorconst x=2; //error//要注意var、let、const声明同名变量本身就会报错
}
function foo(x = 5) {let x = 1; // errorconst x = 2; // error
}
function foo(x, x, y) {// ...
}    // 不报错
function foo(x, x, y = 1) {// ...
} // 报错  SyntaxError: Duplicate parameter name not allowed in this context
对象的解构赋值默认值和函数参数是不一样的
只使用了对象的解构赋值默认值,没有使用函数参数的默认值,那么调用的时候一定要有参数,没有参数则会报错。只有当函数foo的参数是一个对象时,变量x和y才会通过解构赋值生成。如果函数foo调用时没提供参数,变量x和y就不会生成,从而报错。通过提供函数参数的默认值,就可以避免这种情况
function foo({x, y = 5}) {console.log(x, y);
}
foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefinedfunction foo({x, y = 5} = {}) {console.log(x, y);
}
foo() // undefined 5   如果没有参数,那么是函数默认参数即空对象,然后解构赋值。function fetch(url, { body = '', method = 'GET', headers = {} }) {console.log(method);
}
fetch('http://example.com', {})  // "GET"
fetch('http://example.com')  // 报错,第二个参数没有默认值function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {console.log(method);
}
fetch('http://example.com')  // "GET"有,第二个参数具有默认值,是一个空对象,然后进行解构赋值。
1.对象参数才会具有双重默认参数
2.对象参数未定义默认函数参数,当函数调用无参数的时候会报错,找不到函数参数。
3.关键看函数调用:当函数调用具有参数的时候,默认函数参数无作用,解构参数有作用,用来补足剩余对象里面的参数默认值;当函数调用无参数的时候,默认函数参数和解构参数均具有作用,默认函数参数先生效,然后才是默认解构参数生效(补充作用,有则不用,无则补充)
// 写法一
function m1({x = 0, y = 0} = {}) {return [x, y];
}
// 写法二
function m2({x, y} = { x: 0, y: 0 }) {return [x, y];
}
//写法三
function m3({x=2, y=4} = { x: 0}) {return [x, y];
}
// 函数没有参数的情况,先是默认函数参数生效,然后是默认解构参数生效!
console.log('无参数情况', m1()) // [0, 0]
console.log(m2()) // [0, 0]
console.log(m3()) // [0,4] 默认参数有x的默认值,没有y的默认值,那么从默认解构参数获取
// x 和 y 都有值的情况;即有参数,那么只有默认解构参数生效
console.log('x y 都有值的情况', m1({x: 3, y: 8})) // [3, 8]
console.log(m2({x: 3, y: 8})) // [3, 8]
console.log(m3({x: 3, y: 8})) // [3, 8]
// x 有值,y 无值的情况;即有参数,那么只有默认解构参数生效
console.log('x有值,y无值的情况', m1({x: 3})) // [3, 0]  默认解构参数,y有值,则补充
console.log(m2({x: 3})) // [3, undefined]  默认解构参数,y没有值,则为undefined
console.log(m3({x: 3})) // [3, 4]
// x 和 y 都无值的情况;即有参数,那么只有默认解构参数生效
console.log('x和y都无值,即参数是空对象情况', m1({})) // [0, 0];
console.log(m2({})) // [undefined, undefined]  默认解构参数无值,则都为undefined
console.log(m3({})) // [2, 4]
// 传入未定义的z属性的情况;即有参数,只是为空对象,那么只有默认解构参数生效
console.log('传入未定义的z属性的情况', m1({z: 3})) // [0, 0]
console.log(m2({z: 3})) // [undefined, undefined]
console.log(m3({z: 3})) // [2, 4]

2.参数默认值位置
虽然我觉得写代码的时候这个东西没有意义,但是用于学习还是可以了解以下的。你写的时候直接把默认参数放后面就行了。

有默认值的参数如果不是尾参数,该参数传参时不能被省略跳过,且除非显式输入undefined否则无法使用默认参数。
// 例一,默认参数的位置在最前面
function f(x = 1, y) {return [x, y];
}
f() // [1, undefined]
f(2) // [2, undefined]
f(, 1) // 报错,注意不能省略默认参数
f(undefined, 1) // [1, 1] 只能使用undefined来出发默认参数值
// 例二,默认参数在中间
function f(x, y = 5, z) {return [x, y, z];
}
f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, 3)
f(1, ,2) // 报错,注意不能省略默认参数
f(1, undefined, 2) // [1, 5, 2]   只能使用undefined来出发默认参数值
// 例三,默认参数在头尾
function f(x = 1, y, z = 3){return [x, y, z];
}
f() // [1, undefined, 3]
f(2) // [2, undefined, 3]
f(3, 2) // [3, 2, 3]
f(4, ,5) // 报错,注意不能省略默认参数
f(undefined, 2, undefined) // [1, 2, 3]
// 例四,null赋值
function foo(x = 5, y = 6) {console.log(x, y);
}
foo(undefined, null)  // 5 null  默认参数只能undefined触发,null实际上是赋值即传参,并不会触发默认值

3.函数的length属性,Function.length

length 是函数对象的一个属性值,指该函数有多少个必须要传入的参数,即形参的个数。形参的数量不包括剩余参数个数,仅包括第一个具有默认值之前的参数个数。与之对比的是,  arguments.length 是函数被调用时实际传参的个数。因为length属性的含义是,该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。同理,后文的 rest 参数也不会计入length属性。
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
(function(...args) {}).length // 0
(function (a = 0, b, c) {}).length // 0  第一个具有默认参数之前的参数个数是0
(function (a, b = 1, c) {}).length // 1  第一个具有默认参数之前的参数个数是1

4.作用域
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域,这种语法行为,在不设置参数默认值时,是不会出现的。

函数的参数声明了x,所以函数内的x和全局的x无关
let x = 1;
function f(x, y = x) {console.log(y);
}
f() // undefined,x没有默认值
f(2) // 2y的默认值是x,这时函数内x是没有定义的,从作用域往上找,找到全局变量x。y的默认值和函数内部声明的x无关。
let x = 1;
function f(y = x) {let x = 2;console.log(y);
}
f() // 1所以显然如果全局变量没有x,会报错
function f(y = x) {let x = 2;console.log(y);
}
f() // ReferenceError: x is not defined上面给的链接那个人还写了一种参数x=x,我觉得这没意义,没有人真的这么写,想也知道会出问题。回调函数,当参数是函数时
// example 1:
let foo = 'outer';
function bar(func = () => foo) {let foo = 'inner';console.log(func());
}
bar(); // outer
/*
函数bar的参数func的默认值是一个匿名函数,返回值为变量foo。函数参数的作用域里面,并没有定义变量foo,往上找,所以foo指向外层的全局变量foo,因此输出outer。*/// example 2:
function bar(func = () => foo) {let foo = 'inner';console.log(func());
}
bar() // ReferenceError: foo is not defined
/* 匿名函数里面的foo指向函数外层,但是函数外层并没有声明变量foo,所以就报错了。*/// example 3:
var x = 1;
function foo(x, y = function() { x = 2; }) {var x = 3;y();console.log(x);
}
foo() // 3
x // 1
/*
函数foo的参数形成一个单独作用域。这个作用域里面,首先声明了变量x,然后声明了变量y,y的默认值是一个匿名函数。
这个匿名函数内部的变量x没有声明,往上找,在函数参数作用域这找到x声明,就指向第一个参数x。
函数foo内部又声明了一个内部变量x,该变量与第一个函数参数x由于不是同一个作用域,函数参数x的作用域是内部变量x的上一级,所以不是同一个变量,就好像你修改全局变量,函数内部变量不会受到影响。
因此执行y后,内部变量x和外部全局变量x的值都没变,变的是函数参数x。*/// example 4:
var x = 1;
function foo(x, y = function() { x = 2; }) {x = 3;y();console.log(x);
}
foo() // 2
x // 1
/*
如果将var x = 3的var去除,函数foo的内部变量x就指向第一个参数x,与匿名函数内部的x是一致的,
所以最后输出的就是2,而外层的全局变量x依然不受影响。*/

5.函数的name属性

返回该函数的函数名
var f = function () {};
f.name //"f"
(function(){}).name //""
const foo = function fun() {};
foo.name //"fun"

构造函数

原型链

prototype:函数特有,函数.prototype指向函数的原型对象
_proto_:对象都有,指向原型对象,指向函数.prototype(这个不能正常显示,一边两个下划线)
constructor:函数原型对象的constructor指向创建对象的函数,注意修正指向的时候不要加()

蓝色的线就是原型链

1、prototype

1、添加
function Person(age) {this.age = age
}
var person1 = new Person()
var person3 =new Person()
person3.name='bob'
Person.prototype.name = 'kavin'
var person2 = new Person()
console.log(person1.name) //kavin
console.log(person2.name) //kavin
console.log(person3.name) //bob
调用实例对象属性时,先在实例对象查找,没有就顺着原型链查找。
person1、2实际都只有age属性,为什么只有age看下面的new过程,他的name属性是在原型链上找到的
而person3自己有就不用走原型链2、修改
function Person() {function a(){console.log(2)}
}
var person1 = new Person()
var person3 =new Person()
person3.a=function(){console.log(4)}
Person.prototype.a=()=>console.log(3)
var person2 = new Person()
person1.a() //3
person2.a() //3
person3.a() //4

2、__proto__一边有两个下划线

function Person() {this.age=18
}
var person1 = new Person()
var person3 = new Person()
Person.prototype.age=19
var person2 = new Person()
person3.__proto__.age=17
console.log(person1.age) //18
console.log(person2.age) //18
console.log(person3.age) //18
因为自己有function Person() {this.age=18
}
var person1 = new Person()
var person3 = new Person()
Person.prototype.name='Tom'
var person2 = new Person()
person3.__proto__.name='Bob'
console.log(person1.name) //Bob
console.log(person2.name) //Bob
console.log(person3.name) //Bob
因为都没有,就要找链,person3.__proto__和Person.prototype是一个东西

new过程

1.创建空对象,空对象_proto_指向函数原型变量prototype
2.call / apply方法把this指向空对象,传参
3构造函数没返回值,返回对象的属性方法,如果函数return的是非对象(数字、字符串、布尔类型等)会忽略返回值,返回this即空对象; 如果return的是对象,则返回该对象

简易实现
let myNew = function (fn) {if (typeof ctor !== 'function') {throw 'newOperator function the first param must be a function';}//let o={};//o.__proto__ = fn.prototype//o.__proto__.constructor = fnlet o = Object.create(fn.prototype);//从fn的原型对象继承let arg = Array.prototype.slice.call(arguments, 1);//获取参数//let arg=[].slice.call(arguments, 0);let ret = fn.apply(o, arg);//绑定thislet isObject = typeof ret === "object" && ret !== null;//返回类型不是空对象let isFunction = typeof ret === "function";//返回类型是函数return isObject || isFunction ? ret : o;//是对象返回对象,否则返回新建的对象
}
var Person = function() {this.name = "程序员";
};
var p = new Person();
执行顺序
先执行new关键字,创建空对象,空对象继承函数原型变量Person.prototype,再通过call/apply把函数参数传给空对象,函数this获取空对象,this给空对象添加name属性,函数结束,返回this,把this交给变量p// 返回一个对象的 return
var ctr = function() {this.name = "赵晓虎";this.age=18;return {name:"牛亮亮"};
};
// 创建对象
var p = new ctr();
// 访问name属性
console.log(p.name); //"牛亮亮"
console.log(p.age); //undefined
因为返回了对象,所以p={name:"牛亮亮"},这个对象没有age属性//返回非对象数据的构造器,实际返回this
var ctr = function() {this.name = "赵晓虎";this.age=18;return "牛亮亮"
};
// 创建对象
var p = new ctr();
// 访问name属性
console.log(p.name); //"赵晓虎"
console.log(p.age); //18

嵌套函数

嵌套函数如不作为返回值返回,在外部是接触不到的

function a(){function b(){console.log('b')}console.log('a')
}
a.b() //报错a.b不是函数
a()() //报错a(...)不是函数
b() //报错b未定义

构造函数的嵌套函数调用

function a(){this.foo=function(){console.log('foo'+this)}console.log('a'+this)
}
a // 直接返回函数
/*ƒ a(){this.foo=function(){console.log('foo'+this)}console.log('a'+this)
}*/a() //a[object Window]
返回值undefineda.foo() //报错 a.foo is not a functiona().foo() //a[object Window] 然后报错 Cannot read property 'foo' of undefined
从a()可以看到他执行完没有返回东西,所以从undefined的原型对象找不到foonew a() //a[object Object]
返回了对象 a {foo: ƒ}new a().foo() //a[object Object] foo[object Object]
返回值undefined
这个为什么成功找到foo了呢,我的理解是new创建了空对象所以new a()能返回对象a{foo:f},然后对象a的foo属性调用new new a().foo() //a[object Object] foo[object Object]
返回了对象 foo {}
这就可以印证我上面的猜想,,new创建了空对象,new a()返回对象a{foo:f},然后对象a的foo函数和空对象绑定返回如果哪里想错了希望大佬教一下

闭包

没有准确权威的定义,大概为三个条件:访问所在作用域、函数嵌套、在所在作用域外被调用

function a(){var i=0;function b(){i++console.log(i)}return b;
}
var c=a();
c() //1
c() //2
c() //3
var d=a()
d() //1
d() //2
d() //3function a(){var i=0;function b(){i++console.log(i)}return b;
}
a()() //1
a()() //1
var fn = null;
function foo() {var a = 2;function innnerFoo() {console.log(a);}fn = innnerFoo; // 将 innnerFoo的引用,赋值给全局变量中的fn
}
function bar() {fn(); // 此处的保留的innerFoo的引用
}
foo();
bar(); // 2虽然例子中的闭包被保存在了全局变量中,但是闭包的作用域链并不会发生任何改变
var fn = null;
function foo() {var a = 2;function innnerFoo() {console.log(c); // 在这里,试图访问函数bar中的c变量,会抛出错误console.log(a);}fn = innnerFoo; // 将 innnerFoo的引用,赋值给全局变量中的fn
}function bar() {var c = 100;fn(); // 此处的保留的innerFoo的引用
}
foo();
bar();

回调函数

箭头函数

(1)如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
(2)如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。如果只有一句可以不用大括号和return语句
(3)由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
(4)箭头函数可以与变量解构结合使用
(5)箭头函数内部的this是词法作用域,由上下文确定。(词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的),由于this在箭头函数中已经按照词法作用域绑定了,所以,用call()或者apply()调用箭头函数时,无法对this进行绑定,即传入的第一个参数被忽略

const isEven = n => n % 2 === 0;
const square = n => n * n;var f = () => 5;
// 等同于
var f = function () { return 5 };var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {return num1 + num2;
};let getTempItem = id => { id: id, name: "Temp" };  // 报错
let getTempItem = id => ({ id: id, name: "Temp" });  // 不报错const full = ({ first, last }) => first + ' ' + last;
// 等同于
function full(person) {return person.first + ' ' + person.last;
}var Person = {firstName:'hello',
lastName:'world',
getFullName:function(){console.log(this)
var first=this.firstName
var fun=()=>{console.log(this)
return this.firstName+this.lastName
}
return fun()
}
}
Person.getFullName()
//{firstName: "hello", lastName: "world", getFullName: ƒ}
//{firstName: "hello", lastName: "world", getFullName: ƒ}

综合案例

在这里插入代码片

this

window:
(嵌套)函数独立调用、IIFE立即执行函数、闭包、隐式丢失

test()是独立调用,foo()是方法调用

function foo(){console.log(this==window);
}
foo() //truevar a = 0
var obj = {a: 2,foo: function () {function test() {console.log(this.a)}test()console.log(this.a)}
}
obj.foo() //0 2

立即调用

var a = 0
var obj = {a: 2,foo: function () {(function test() {console.log(this.a)})()console.log(this.a)}
}
obj.foo() //0 2

由于闭包this指向window,而常常需要访问嵌套函数this,所以常用var that=this,闭包中通过作用域查找找到嵌套函数this

var a = 0
var obj = {a: 2,foo: function () {var that = thisfunction test() {console.log(this.a)}return test}
}
obj.foo()() //0var a = 0
var obj = {a: 2,foo: function () {var that = thisfunction test() {console.log(that.a)}return test}
}
obj.foo()() //2

被隐式绑定的函数丢失绑定对象,从而默认绑定到window

var a = 0;
function foo(){console.log(this.a);
};
var obj = {a : 2,foo:foo
}
//把obj.foo赋予别名bar,造成了隐式丢失,因为只是把foo()函数赋给了bar,而bar与obj对象则毫无关系
var bar = obj.foo;
bar(); //0var a = 0;
function foo(){console.log(this.a);
};
function bar(fn){fn();
}
var obj = {a : 2,foo:foo
}
//把obj.foo当作参数传递给bar函数时,有隐式的函数赋值fn=obj.foo。
//与上例类似,只是把foo函数赋给了fn,而fn与obj对象则毫无关系
bar(obj.foo); //0var a = 0;
function foo(){console.log(this.a);
};
var obj = {a : 2,foo:foo
}
setTimeout(obj.foo,100);//0

对象:
方法调用

var a = 0;
function foo() {console.log(this.a);
};
var obj = {a: 2,foo: foo
}
var p = {a: 3
}
obj.foo() //2
p.foo = obj.foo
p.foo() //3

call、apply绑定:

事件循环

它从script(整体代码)开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的micro-task。当所有可执行的micro-task执行完毕之后。循环再次从macro-task开始,找到其中一个任务队列执行完毕,然后再执行所有的micro-task,这样一直循环下去。

任务队列

  • 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
  • 主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。
  • 一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
  • 主线程不断重复上面的第三步。

一个 Event Loop 中,可以有一个或者多个任务队列(task queue),一个任务队列便是一系列有序任务(task)的集合;每个任务都有一个任务源(task source),源自同一个任务源的 task 必须放到同一个任务队列,从不同源来的则被添加到不同队列。 setTimeout/Promise 等 API 便是任务源,而进入任务队列的是他们指定的具体执行任务。

异步

异步中任务队列的执行顺序: 先微任务microtask队列,再宏任务macrotask队列
微任务队列
Promise.then,process.nextTick
宏任务队列
setTimeout, setInterval, setImmediate, I/O, UI rendering

process.nextTick
指定的任务总是发生在所有异步任务之前,当前主线程的末尾,即当前”执行栈”的尾部–>下一次 Event Loop(主线程读取”任务队列”)之前–>触发 process 指定的回调函数。

Promise

创建(new)Promise时,作为Promise参数传入的函数是会被立即执行的,只是其中执行的代码可以是异步代码。当resolve的参数是一个Promise对象时,resolve会"拆箱"获取这个Promise对象的状态和值,但这个过程是异步的,第二个参数reject不具备”拆箱“的能力,reject的参数会直接传递给then方法中的rejected回调。

这三种状态不受外界影响,而且状态只能从pending改变为resolved或者rejected,并且不可逆。在Promise对象的构造函数中,将一个函数作为第一个参数。而这个函数,就是用来处理Promise的状态变化。

var p = new Promise(function(resolve, reject){resolve(1);
});
p.then(function(value){               //第一个thenconsole.log(value);return value*2;
}).then(function(value){              //第二个thenconsole.log(value);
}).then(function(value){              //第三个thenconsole.log(value);return Promise.resolve('resolve');
}).then(function(value){              //第四个thenconsole.log(value);return Promise.reject('reject');
}).then(function(value){              //第五个thenconsole.log('resolve: '+ value);
}, function(err){console.log('reject: ' + err);
})控制台输出:
1
2
undefined
"resolve"
"reject: reject"

Promise对象的then方法返回一个新的Promise对象,因此可以通过链式调用then方法。then方法接收两个函数作为参数,第一个参数是Promise执行成功时的回调,第二个参数是Promise执行失败时的回调。两个函数只会有一个被调用,函数的返回值将被用作创建then返回的Promise对象
return 一个同步的值 ,或者 undefined(当没有返回一个有效值时,默认返回undefined),then方法将返回一个resolved状态的Promise对象,Promise对象的值就是这个返回值。
return 另一个 Promise,then方法将根据这个Promise的状态和值创建一个新的Promise对象返回。
throw 一个同步异常,then方法将返回一个rejected状态的Promise, 值是该异常。

根据以上分析,代码中第一个then会返回一个值为2(1*2),状态为resolved的Promise对象,于是第二个then输出的值是2。第二个then中没有返回值,因此将返回默认的undefined,于是在第三个then中输出undefined。第三个then和第四个then中分别返回一个状态是resolved的Promise和一个状态是rejected的Promise,依次由第四个then中成功的回调函数和第五个then中失败的回调函数处理。

var p1 = new Promise( function(resolve,reject){foo.bar();resolve( 1 );
});p1.then(function(value){console.log('p1 then value: ' + value);},function(err){console.log('p1 then err: ' + err);}
).then(function(value){console.log('p1 then then value: '+value);},function(err){console.log('p1 then then err: ' + err);}
);var p2 = new Promise(function(resolve,reject){resolve( 2 );
});p2.then(function(value){console.log('p2 then value: ' + value);foo.bar();}, function(err){console.log('p2 then err: ' + err);}
).then(function(value){console.log('p2 then then value: ' + value);},function(err){console.log('p2 then then err: ' + err);return 1;}
).then(function(value){console.log('p2 then then then value: ' + value);},function(err){console.log('p2 then then then err: ' + err);}
);控制台输出:
"p1 then err: ReferenceError: foo is not defined"
"p2 then value: 2"
"p1 then then err: undefined"
"p2 then then err: ReferenceError: foo is not defined"
"p2 then then then value: 1"

Promise中的异常由then参数中第二个回调函数(Promise执行失败的回调)处理,异常信息将作为Promise的值。异常一旦得到处理,then返回的后续Promise对象将恢复正常,并会被Promise执行成功的回调函数处理。另外,需要注意p1、p2 多级then的回调函数是交替执行的 ,这正是由Promise then回调的异步性决定的。
Promise接收的函数参数是同步执行的,但then方法中的回调函数执行则是异步的

var p1 = Promise.resolve( 1 );
var p2 = Promise.resolve( p1 );
var p3 = new Promise(function(resolve, reject){resolve(1);
});
var p4 = new Promise(function(resolve, reject){resolve(p1);
});console.log(p1 === p2);
console.log(p1 === p3);
console.log(p1 === p4);
console.log(p3 === p4);p4.then(function(value){console.log('p4=' + value);
});p2.then(function(value){console.log('p2=' + value);
})p1.then(function(value){console.log('p1=' + value);
})输出台输出:
true
false
false
false
p2=1
p1=1
p4=1

Promise.resolve(…)可以接收一个值或者是一个Promise对象作为参数。当参数是普通值时,它返回一个resolved状态的Promise对象,对象的值就是这个参数;当参数是一个Promise对象时,它直接返回这个Promise参数。因此,p1 === p2。但通过new的方式创建的Promise对象都是一个新的对象,因此后面的三个比较结果都是false。另外,为什么p4的then最先调用,但在控制台上是最后输出结果的呢?因为p4的resolve中接收的参数是一个Promise对象p1,resolve会对p1”拆箱“,获取p1的状态和值,但这个过程是异步的,可参考下面。

var p1 = new Promise(function(resolve, reject){resolve(Promise.resolve('resolve'));
});var p2 = new Promise(function(resolve, reject){resolve(Promise.reject('reject'));
});var p3 = new Promise(function(resolve, reject){reject(Promise.resolve('resolve'));
});p1.then(function fulfilled(value){console.log('fulfilled: ' + value);}, function rejected(err){console.log('rejected: ' + err);}
);p2.then(function fulfilled(value){console.log('fulfilled: ' + value);}, function rejected(err){console.log('rejected: ' + err);}
);p3.then(function fulfilled(value){console.log('fulfilled: ' + value);}, function rejected(err){console.log('rejected: ' + err);}
);控制台输出:
"rejected: [object Promise]"
"fulfilled: resolve"
"rejected: reject"

Promise回调函数中的第一个参数resolve,会对Promise执行"拆箱"动作。即当resolve的参数是一个Promise对象时,resolve会"拆箱"获取这个Promise对象的状态和值,但这个过程是异步的。p1"拆箱"后,获取到Promise对象的状态是resolved,因此fulfilled回调被执行;p2"拆箱"后,获取到Promise对象的状态是rejected,因此rejected回调被执行。但Promise回调函数中的第二个参数reject不具备”拆箱“的能力,reject的参数会直接传递给then方法中的rejected回调。因此,即使p3 reject接收了一个resolved状态的Promise,then方法中被调用的依然是rejected,并且参数就是reject接收到的Promise对象

SetTimeout

函数体内容立即执行,第一个参数延迟执行

例子

setTimeout(function() {console.log('timeout1');
})
new Promise(function(resolve) {console.log('promise1');for(var i = 0; i < 1000; i++) {i == 99 && resolve();}console.log('promise2');
}).then(function() {console.log('then1');
})
console.log('global1');
结果:
promise1
promise2
global1
then1
timeout1

解析:
首先setTimeout入setTimeout队列,然后Promise的第一个参数new的时候执行,then入micro-task的Promise队列,接着往下输出了globa1,然后,全局任务就执行完毕了,开始执行微任务,输出then1,结束第一轮循环,开始第二轮,执行宏任务输出setTimeout1

ajax

AJAX 是异步的 JavaScript 和 XML(Asynchronous JavaScript And XML)。简单点说,就是使用 XMLHttpRequest 对象与服务器通信。 它可以使用 JSON,XML,HTML 和 text 文本等格式发送和接收数据。AJAX 最吸引人的就是它的“异步”特性,也就是说他可以在不重新刷新页面的情况下与服务器通信,交换数据,或更新页面

前端学习日志-4-js相关推荐

  1. 前端学习笔记(js基础知识)

    前端学习笔记(js基础知识) JavaScript 输出 JavaScript 数据类型 常见的HTML事件 DOM 冒泡与捕获 流程控制语句 for..in 计时器 let,var,const的区别 ...

  2. WEB前端学习日志Day4

    WEB前端学习日志Day4 今日总结:通过一天的学习了解了样式表的权重,css的层叠性,css的选择符,划分网页上下布局,主要对css的选择符进行深入理解和代码实现. 样式表的权重 样式表的权重关系: ...

  3. 前端学习日志(Vue)

    文章目录 模板语法 插值语法 指令语法 数据绑定 MVVM模型 数据代理 事件处理 计算属性 监视属性 进行监听 深度监视 条件渲染 列表渲染 1.基本列表 列表过滤 列表排序 生命周期 Vue组件的 ...

  4. JS学习日志15 -- JS基础--忍者代码

    前言 从头开始对javascript进行学习,每天定个小目标,学习一点,期待学习完后,对js的认知会发生什么变化~~ :JS基础知识 一.忍者代码 过去的程序员忍者使用这些技巧,来使代码维护者的头脑更 ...

  5. 前端学习:Vue.js基本使用

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. Vue教程文档:  https://cn.vuejs.org/v2/guide/ 定义 实例: ne ...

  6. 前端学习之touch.js与swiper学习

    Touch.js是移动设备上的手势识别与事件库,改框架基于原生js,操作简单,主要分drag,swipe,rotate,scale,tab,hold,touch操作. swiper是一个移动端触摸滑动 ...

  7. 【前端学习】Day-16 JS基础、循环、函数、数组、字符串、字典

    文章目录 1. 了解JavaScript 2. js小案例 3. JavaScript基础 4. js循环 5. js函数 6. js数组 7. js字符串 8. js练习题 1. 了解JavaScr ...

  8. 前端学习笔记——node.js

    初识 Node.js 什么是 Node.js Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境. Node.js 中的 JavaScript 运行环境 注意: 浏 ...

  9. 【前端学习日志】HTML表格表单注册页面案例+CSS选择器

    今日学习 一.HTML部分 1.表格的基本语法 2.表头单元格标签 3.还是表头单元格标签 4.小说排行榜案例 5.合并单元格 6.无序列表 7.有序列表 8.自定义列表 9.表单域 10.Input ...

最新文章

  1. Solr 3.5:配置mmseg4j同义词(已经配置好中文分词)
  2. SharePoint Server 2007 Web内容管理中的几个关键概念
  3. Unix toolbox注解2之Linux系统状态用户和限制
  4. python教程:apscheduler模块使用教程
  5. 阿里员工都在用的知识管理工具,究竟有何特别?
  6. 微软工程师测试题——未来
  7. 如何在linux环境下安装kvm,如何在Linux发行版上安装和配置KVM和Open vSwitch?
  8. vba excel 退出编辑状态_偷梁换柱之EXCEL编辑保护和VBA隐藏代码保护的解锁
  9. 深入理解CSS六种颜色模式
  10. dbForge mysql数据库比对
  11. 令人激动!谷歌推强化学习新框架「多巴胺」,基于TensorFlow,已开源丨附github...
  12. 2gt;MSVCRTD.lib(MSVCR100D.dll) : error LNK2005: _calloc 已经在 LIBCMTD.lib(dbgcalloc.obj) 中定义...
  13. esp8266电池供电方案_(普通照明、应急照明、事故照明)方案解读
  14. 步步为赢,做好数据分析的7个步骤
  15. Kafka Exception:Bootstrap broker disconnected Consumer disconnected
  16. ChatGPT所有插件详细教程
  17. 华为 ensp 部分查询方法
  18. Linux下minikube启动失败(It seems like the kubelet isn't running or healthy)
  19. android手机慢,揭秘Android手机变慢的三大原因与对策
  20. 美拍高颜值短视频一键解析批量保存到电脑中

热门文章

  1. GeneMark-ES:真核生物编码基因预测软件
  2. EOS智能合约开发系列(12): 多签合约代码分析(一)
  3. 【信息安全-科软课程】Lab2环境变量和Set-UID程序实验
  4. vue+gin—— GetcharZp
  5. Vue表单数据修改与删除
  6. linux mv命令的功能,linux常用命令:Linux常用命令之mv命令是什么?
  7. CSS3中steps()动画的详解
  8. 【python】Flask之路由
  9. 显示空间——字符显示之矢量文字
  10. 4.3 协方差及相关系数、矩