参考链接:http://es6.ruanyifeng.com/
function Student(name) {
this.name = name
}
let studentA = new Student('a')
console.log(studentA)
// 等价于
let studentB = (function() {
let obj = {}
obj.__proto__ = Student.prototype
Student.call(obj, 'b')
return obj
})()
console.log(studentB)
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
fun.call(thisArg, arg1, arg2, ...)
apply() 方法调用一个具有给定 this 值的函数,以及作为一个数组(或类似数组对象)提供的参数。当不确定参数的个数时,就可以使用 apply。
func.apply(thisArg, [argsArray])
bind() 方法会返回一个新的函数,在 bind() 被调用时,这个新函数会 call 原来的函数,新函数的 this 被 bind 的第一个参数指定,其余的参数将作为新函数的参数供调用时使用。
function.bind(thisArg[,arg1[,arg2[, ...]]])
其实 apply 和 call 基本类似,他们的区别只是传入的参数不同。call 方法接受的是若干个参数列表,而 apply 接收的是一个包含多个参数的数组。
bind 和 call、apply 方法作用是一致的,只是该方法会返回一个新函数,并且我们必须要手动去调用。
它的基本思想是,网页通过添加一个 <script> 元素,向服务器请求 JSON 数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。
请求方:xxx.com的前端程序员(浏览器)
响应方:yyy.com的后端程序员(服务器)
为什么不支持POST?
代码实现 zhouwanwen.com:8001 和 jack.com:8002 之间的 JSONP 请求:Github - nodejs-demo-1
随它去吧 - 说说JSON和JSONP,也许你会豁然开朗,含jQuery用例
eval() 函数会将传入的字符串当做 JavaScript 代码进行执行。
eval() 是全局对象的一个函数属性
0.1 + 0.2 // 0.30000000000000004
因为 JavaScript 存储数值采用双精度浮点数,会出现精度丢失的问题。
0.100000000000000000000002 === 0.1 // true
console.log(0.100000000000000002) // 0.1
如何使得 0.1+0.2===0.3 呢?
parseFloat((0.1 + 0.2).toFixed(10)) === 0.3 // true
掘金 - 0.1 + 0.2不等于0.3?为什么JavaScript有这种“骚”操作?
Object.is() 方法如果满足以下条件则两个值相等:
Object.is('hello','hello') // true
Object.is('hello','hi') // false
Object.is([],[]) // false
Object.is(null,null) // true
Object.is(null,undefined) // false
Object.is(0,+0) // true
Object.is(0,-0) // false
Object.is(-0,+0) // false
Object.is(NaN,0/0) // true
扩展:
let obj = {
name: 'LqZww'
}
if (obj.age == null) {}
// 等价于
if (obj.age === null || obj.age === undefined) {}
typeof 可用于基本数据类型的类型判断,例如:number、string、boolean、function、undefined 等,返回值都是小写的字符串。
console.log(typeof undefined) // undefined
console.log(typeof 1) // number
console.log(typeof "a") // string
console.log(typeof true) // boolean
console.log(typeof Symbol()) // symbol
console.log(typeof function(){}) // function
console.log(typeof new Function()) // function
console.log(typeof null) // object
console.log(typeof [1,2]) // object
console.log(typeof {}) // object
console.log(typeof new String()) // object
console.log(typeof new Object()) // object
console.log(typeof new Number()) // object
instanceof 是判断变量是否为某个对象的实例,返回值为 true 或 false。
var arr = []
var obj = {}
console.log(arr instanceof Array) // true
console.log(arr instanceof Object) // true
console.log(obj instanceof Array) // false
console.log(obj instanceof Object) // true
这种方法存在一个问题:能正确处理的对象只有 Number、String、Array 等能够被 JSON 表示的数据结构,因此像函数、undefined、正则表达式、引用这种不能被 JSON 表示的类型将不能被正确处理。
let obj = {
name: 'LqZww',
age: 18,
like: {
game: 'TLBB',
ear: 'rou'
},
arr: [1, 2]
};
let deepClone = JSON.parse(JSON.stringify(obj));
deepClone.name = 'lq';
deepClone.like.game = 'LOL';
console.log(obj); // {name: "LqZww", age: 18, like: {…}, arr: Array(2)}
console.log(deepClone); // {name: "lq", age: 18, like: {…}, arr: Array(2)}
console.log(obj === deepClone); // false
let obj = {
name: 'LqZww',
age: 18,
like: {
game: 'TLBB',
ear: 'rou'
},
arr: [1, 2]
};
function deepClone(object) {
let object2
if (!(object instanceof Object)) {
return object
} else if (object instanceof Array) {
object2 = []
} else if (object instanceof Function) {
object2 = eval(object.toString())
} else if (object instanceof Object) {
object2 = {}
}
for (let key in object) {
object2[key] = deepClone(object[key])
}
return object2
}
var obj2 = deepClone(obj)
obj2.name = 'lq';
obj2.like.game = 'LOL';
console.log(obj) // {name: "LqZww", age: 18, like: {…}, arr: Array(2)}
console.log(obj2) // {name: "lq", age: 18, like: {…}, arr: Array(2)}
console.log(obj === obj2) // false
instanceof
instanceof 是判断变量是否为某个对象的实例,返回值为 true 或 false。
Object.prototype.toString.call()
每一个继承 Object 的对象都有 toString 方法,如果 toString 方法没有重写的话,会返回 [Object type],其中 type 为对象的类型。但当除了 Object 类型的对象外,其他类型直接使用 toString 方法时,会直接返回都是内容的字符串,所以我们需要使用 call 或者 apply 方法来改变 toString 方法的执行上下文。该方法对于所有基本的数据类型都能进行判断。
Array.isArray()
此方法用来判断对象是否为数组
let arr = [11, 22, 33]
// 1. pop() 方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。
let arr1 = arr.pop()
console.log(arr) // (2) [11, 22]
console.log(arr1) // 33
// 2. shift() 方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。
let arr2 = arr.shift()
console.log(arr) // (2) [22, 33]
console.log(arr2) // 11
// 3. push() 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。
let arr3 = arr.push(44)
console.log(arr) // (4) [11, 22, 33, 44]
console.log(arr3) // 4
// 4. unshift() 方法将一个或多个元素添加到数组的开头,并返回该数组的新长度。
let arr4 = arr.unshift(1)
console.log(arr) // (4) [1, 11, 22, 33]
console.log(arr4) // 4
// 5. concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
let arr5 = arr.concat([44, 55])
console.log(arr5) // (5) [11, 22, 33, 44, 55]
// 6. slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end),原始数组不会被改变。
let arr6 = arr.slice(2)
console.log(arr6) // [33]
// 7. filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素,不改变原数组。
let arr7 = arr.filter(num => num > 20)
console.log(arr7) // (2) [22, 33]
// 8. map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。
let arr8 = arr.map(num => num * 2)
console.log(arr8) // (3) [22, 44, 66]
// 9. splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
let arr9 = arr.splice(1, 1, 44)
console.log(arr) // (3) [11, 44, 33]
console.log(arr9) // [22]
利用双重循环遍历二维数组中的每一个元素并存放到新的数组中。
var arr = [
['z', 'w', 'w'],
['l', 'q'],
[12, 52, 7]
];
var result = [];
for (var i = 0; i < arr.length; i++) {
for (var a = 0; a < arr[i].length; a++) {
result.push(arr[i][a]);
}
}
console.log(result); // (8) ["z", "w", "w", "l", "q", 12, 52, 7]
利用 concat() 方法来合并两个或多个数组。
var arr = [
['z', 'w', 'w'],
['l', 'q'],
[12, 52, 7]
];
var result = [];
for (var i = 0; i < arr.length; i++) {
result = result.concat(arr[i]);
}
console.log(result); // (8) ["z", "w", "w", "l", "q", 12, 52, 7]
var arr = [
['z', 'w', 'w'],
['l', 'q'],
[12, 52, 7]
];
var result = Array.prototype.concat.apply([], arr);
console.log(result); // (8) ["z", "w", "w", "l", "q", 12, 52, 7]
flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
var arr = [
['z', 'w', 'w'],
['l', 'q'],
[12, 52, 7]
];
var result = arr.flat()
console.log(result) // (8) ["z", "w", "w", "l", "q", 12, 52, 7]
var arr = [
1, [2, 3, [
4, 5, [
6, 7
], 8
], 9], 10
];
var result = []
function reduction(arr) {
arr.forEach(item => {
if (Array.isArray(item)) {
reduction(item)
} else {
result.push(item)
}
})
return result
}
console.log(reduction(arr)); // (10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
flat() 的参数使用 Infinity,可展开任意深度的嵌套数组。
var arr = [
1, [2, 3, [
4, 5, [
6, 7
], 8
], 9], 10
];
var result = arr.flat(Infinity)
console.log(result) // (10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
null 是一个字面量,表示空,没有对象。
用法:
undefined 表示"缺少值",就是此处应该有一个值,但是还没有定义。
用法:
惯例:
let obj = null
,表示空对象。null == undefined // true
null === undefined // false
typeof null // "object"
typeof undefined // "undefined"
1 + null // 1
1 + undefined // NaN
立即执行函数就是说这个函数是立即执行函数体的,不需要额外的去主动调用,要成为立即执行函数,需要满足两个条件:
立即执行函数的目的是 创建独立的作用域,让外部无法访问作用域内部的变量,从而避免 变量污染。
下面这段代码就是一个立即执行函数:
(function(){
console.log("这是立即执行函数")
})()
除了上面这种写法,还有如下写法:
// (匿名函数())
(function(){
console.log("这是立即执行函数")
}())
// !匿名函数()
!function(){
console.log("这是立即执行函数")
}()
// +匿名函数()
+function(){
console.log("这是立即执行函数")
}()
// -匿名函数()
-function(){
console.log("这是立即执行函数")
}()
// ~匿名函数()
~function(){
console.log("这是立即执行函数")
}()
// void 匿名函数()
void function(){
console.log("这是立即执行函数")
}()
// new 匿名函数()
new function(){
console.log("这是立即执行函数")
}()
trim() 方法会从一个字符串的两端删除空白字符。
function trim(string) {
return string.replace(/^\s+|\s+$/g, '')
}
console.log(trim(" LqZww ")) // LqZww
在没有 ES6 class 之前的常规写法:
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function(sing) {
console.log(this.name + "唱了" + sing)
}
var zname = new Person('zww', 22)
console.log(zname) // Person {name: "zww", age: 22}
zname.sayHi('啊哈哈') // zww唱了啊哈哈
在 ES6 中新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实例化对象。
类抽象了对象的公共部分,它泛指某一大类。
对象特指某一个,通过类实例化一个具体的对象。
class Star {
constructor(name, age) {
this.name = name
this.age = age
}
sayHi(sing) {
console.log(this.name + "唱了" + sing);
}
}
var zname = new Star("zww", 11)
console.log(zname); // Star {name: "zww", age: 11}
zname.sayHi("我爱你") // zww唱了我爱你
注意:
JavaScript 中的类可以继承某个类,其中被继承的类称为父类,而继承父类的被称为子类。
子类可以有自己的函数和构造器,当子类中存在父类相同的方法时,则该方法不会从父类继承,而使用子类的方法。
class Father {
constructor(name, age) {
this.name = name
this.age = age
}
sayHi(sing) {
console.log(this.name + '的年龄是' + this.age + ',并且唱了' + sing)
}
}
class Son extends Father {
}
var father = new Father('zww', 18)
console.log(father) // Father {name: "zww", age: 18}
var son = new Son('lq', 22)
console.log(son) // Son {name: "lq", age: 22}
son.sayHi('呵呵') // lq的年龄是22,并且唱了呵呵
super 关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数。
class Father {
constructor(name, age) {
this.name = name
this.age = age
}
sayHi() {
console.log('父类函数')
}
}
class Son extends Father {
constructor(name, age, sex) {
super(name, age)
this.sex = sex
}
sonfn() {
super.sayHi()
console.log('子类函数')
}
}
var son = new Son('lq', 22, '女')
console.log(son) // Son {name: "lq", age: 22, sex: "女"}
son.sonfn('丫丫') // 父类函数 子类函数
注意:
Function.prototype.myCall = function(context) {
if (typeof this !== 'function') {
console.log("error")
}
let args = [...arguments].slice(1)
let result = null
context = context || window
context.fn = this
result = context.fn(...args)
delete context.fn
return result
}
冴羽 - JavaScript深入之call和apply的模拟实现
Function.prototype.myApply = function(context) {
if (typeof this !== 'function') {
throw new TypeError("Error")
}
let result = null
context = context || window
context.fn = this
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
Function.prototype.myBind = function(context) {
if (typeof this !== 'function') {
throw new TypeError("Error")
}
var args = [...arguments].slice(1),
fn = this
return function Fn() {
return fn.apply(this instanceof Fn ? this : context, args.concat(...arguments))
}
}
let xhr = new XMLHttpRequest()
xhr.open('GET', '/xxx', true)
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
console.log('请求成功')
}else{
console.log('请求失败')
}
}
}
xhr.send()
当我们触发事件时,但是一定在事件触发的第 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那么就以新触发事件的时间为准,n 秒后才执行。
就是等你触发完事件 n 秒内不再触发事件才会执行。
function debounce(fn, wait) {
let timer = null
return function() {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => fn.apply(this, arguments), wait)
}
}
冴羽 - JavaScript专题之跟着underscore学防抖
从上一次命令结束开始的一定时间范围 n 秒内,如果多次连续下达命令,则只执行当前时间段 n 秒内第一次命令。
如果你持续触发事件,每隔一段时间,只执行一次事件。
function throttle(fn, gapTime) {
let lastTime = null
let nowTime = null
return function() {
nowTime = Date.now()
if (!lastTime || nowTime - lastTime > gapTime) {
fn()
lastTime = nowTime
}
}
}
冴羽 - JavaScript专题之跟着 underscore 学节流
class EventHub {
private cache: { [key: string]: Array<(data: unknown) => void} = {}
on(eventName: string, fn: (data: unknown) => void) {
this.cache[eventName] = this.cache[eventName] || []
this.cache[eventName].push(fn)
}
emit(eventName: string, data?: unknown) {
let array = this.cache[eventName] || []
array.forEach(fn => {
fn(data)
});
}
off(eventName: string, fn: (data: unknown) => void) {
this.cache[eventName] = this.cache[eventName] || []
let index = indexOf(this.cache[eventName], fn)
if (index === -1) return;
this.cache[eventName].splice(index, 1)
}
}
export default EventHub;
事件委托,其实就是把一个元素响应事件(click、keydown...)的函数委托到另一个元素上。
一般来说,我们会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。
使用事件委托的好处:
async / await语法了解吗,目的是什么
手写一个Promise
联系客服