JS基本和非基本数据类型以及区别
1.目前JS中有6种基本数据类型: Undefined、Null、Boolean、Number、Symbol 和 String。
还有1种复杂的数据类型—Object,Object本质上是由一组无序的名值对组成的。
Object、Array和Function则属于引用类型。
2.将基本数据类型与其值进行比较,这意味着如果两个值具有相同的数据类型并具有相同的值,
那么它们是严格相等的。
3.非基本数据类型不与值进行比较。例如,如果两个对象具有相同的属性和值,则它们严格不相等。
实现 new 运算符
let _new = function(func) {
//创建一个空对象 o,并且继承构造函数的原型对象
let nullObject = Object.create(func.prototype);
//执行构造函数,并且上下文 this 指向 nullObject 对象
let k = func.call(nullObject);
//如果构造函数返回的是对象就返回该对象,否则返回 nullObject 对象
if(typeof k === 'object') {
return k
}else {
return nullObject
}
}
深拷贝、浅拷贝
1、lodash函数库
import { cloneDeep } from 'lodash';//只能拷贝第一层
let obj = {xx: xx}
let newobj = cloneDeep(obj)
2、Array.slice() Array.concat()
let arr = [1,2,3,4]
let newArr = arr.slice(0) //只能深拷贝一层
let newArr1 = [].concat() //只能深拷贝一层
3、JSON.stringify() JSON.parse()
let obj = {name: 'lanbo'}
let newObj = JSON.parse(JSON.stringify(obj))//无法实现属性值为 function 和 undefined 的拷贝,并且拷贝从原型链继承的值也会有问题,比如 constructor 的值变成了 Object
4、for循环加递归
//使用递归,在不使用库的情况下,这种方式可以实现真正的深层度的拷贝
function deepClone(obj) {
let clone = Array.isArray(obj) ? [] : {};
if(obj && typeof obj === 'object') {
for(let key in obj) {
if(obj.hasOwnProperty(key) {
if(obj[key] && typeof obj[key] === 'object') {
clone[key] = deepClone(obj[key]);
}else {
clone[key] = obj[key];
}
}
}
}
return clone;
}
Object.assign()
原文浅谈Object.assign
合并对象,浅拷贝,
为对象添加属性、方法
var target = {a : 1}; //目标对象
var source1 = {b : 2}; //源对象1
var source2 = {c : 3}; //源对象2
var source3 = {c : 4}; //源对象3,和source2中的对象有同名属性c
Object.assign(target,source1,source2,source3);//{a:1,b:2,c:4}
对this的理解
原文说说你对this的理解
谁调用,this指向谁
1、严格模式下this为undefined,非严格模式下指向window。
2、在全局环境中,this 指向 window 对象。
3、ES6 的箭头函数中,this 对象是在函数定义的执行环境。
4、call,bind,apply可以改变this的指向
call bind apply 的区别(改变函数上下文)
call,apply,bind的第一个参数都是this要指向的对象
bind是返回对应函数,便于稍后调用,call与apply是立即调用
call和apply的区别
call(this指向的对象,参数1,参数2,参数3…);
apply(this指向的对象,[参数1,参数2,参数3…]);
bind原理
Function.prototype.bind = function (obj){
let that = this;
return function(){ //返回函数,需要再次调用才能执行改变this指向
that.apply(obj,arguments);
}
}
判断js对象为空
es6新增的Object.keys
var data = {};
var arr = Object.keys(data);
alert(arr.length == 0); //true 为空, false 不为空
使用JSON
var data = {};
var b = (JSON.stringify(data) == "{}");
alert(b); //true 为空, false 不为空
for in 遍历对象
for(var key in obj){ //key是对象的key
return true; //如果不为空,返回true
}
return false; //如果为空,返回false
Object.getOwnPropertyNames()方法
var data = {};
var arr = Object.getOwnPropertyNames(data);
alert(arr.length == 0);//true
js中判断两个对象是否相等
1、JSON.stringify() (如果属性位置调换则为false,慎用!)
let obj1 = {a:1,b:2}
let obj2 = {a:1,b:2}
console.log(JSON.stringify(obj1) === JSON.stringify(obj2)) //true
2、用 Object.keys() 或 Object.getOwnPropertyNames,拿到key和value进行比较,层次深的话需要递归
字面量{} 和 new 出来的对象的区别
1、{ } 代码量少,易懂
2、{ } 运行更快
3、派生类多的时候 new 可以节省代码量
JS中的双等号(==)比较机制!(==隐性转换)
抄自JS中的双等号(==)比较机制!
1、有NaN则为false
2、true == 1 false == 0
3、null == undefined
4、
js的基本规范
1、使用 === 或 !== 进行比较
2、switch 必须带有default分支
3、for if else 必须用大括号
4、语句结束加分号
5、使用驼峰命名
eval
eval()可以接受一个字符串str作为参数,并把这个参数作为脚本代码来执行。
接受的参数:
1、如果参数是一个表达式,eval() 函数将执行表达式;
2、如果参数是Javascript语句,eval()将执行 Javascript 语句
eval("let a = 1");//声明一个变量a并赋值1。
eval("2 + 3");//执行加运算,并返回运算值。
eval("myFun()");//执行myFun()函数。
eval("{b:2}");
//声明一个对象。如果想返回此对象,则需要在对象外面再嵌套一层小括如下eval("({b:2})");
注:使用eval来解析JSON格式字符串的时候,会将{}解析为代码块,而不是对象的字面量
setTimeout
//第一个参数为function,也可为字符串同eval
setTimeout(function, delay, param1)
- setTimeout返回值 timeoutID 是一个正整数,表示由 setTimeout() 调用创建的定时器的编号。这个值可以传递给 clearTimeout() 来取消该定时器。
- delay为字符串时自动转换为数字,无法转换则为0
- setTimeout中的第一个入参函数中的this指向window,
解决方法:
bind改变this指向
const myArray = ["zero", "one", "two"];
const myBoundMethod = function (sProperty) {
console.log(arguments.length > 0 ? this[sProperty] : this);
}.bind(myArray);
setTimeout(myBoundMethod, 1.0 * 1000); // 因为 'this' 在函数中绑定到了 myArray,还是在 1 秒后输出 "zero,one,two"
包装函数
setTimeout(() => {
myArray.myMethod();
}, 2.0 * 1000); // 在 2 秒后输出 "zero,one,two"
4.嵌套调用第五次及后面延迟为最小4毫秒,前四次为0或1毫秒
闭包
闭包指的是一个函数可以访问另一个函数作用域中的变量。
常见的构造方法,是在一个函数内部定义另外一个函数。内部函数可以引用外层函数的变量;
外层变量不会被垃圾回收机制回收。
优点:避免全局变量污染。
缺点:容易造成内存泄露(解决:手动赋值null)
函数柯里化
将需要多个参数的函数用
只有一个参数的函数通过递归将参数依次处理(柯里化)来代替
内存管理(垃圾回收机制)
1、引用计数垃圾收集(已废弃)
此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。
var o = {
a: {
b: 2,
},
};
// 两个对象被创建,一个作为另一个的属性被引用,另一个被分配给变量 o
// 很显然,没有一个可以被垃圾收集
var o2 = o; // o2 变量是第二个对“这个对象”的引用
o = 1; // 现在,“这个对象”只有一个 o2 变量的引用了,“这个对象”的原始引用 o 已经没有
var oa = o2.a; // 引用“这个对象”的 a 属性
// 现在,“这个对象”有两个引用了,一个是 o2,一个是 oa
o2 = "yo"; // 虽然最初的对象现在已经是零引用了,可以被垃圾回收了
// 但是它的属性 a 的对象还在被 oa 引用,所以还不能回收
oa = null; // a 属性的那个对象现在也是零引用了
// 它可以被垃圾回收了
弊端:循环使用则不会被回收
function f() {
var o = {};
var o2 = {};
o.a = o2; // o 引用 o2
o2.a = o; // o2 引用 o
return "azerty";
}
f();
2、标记 - 清除算法
这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。
这个算法假定设置一个叫做根(root)的对象(在 Javascript 里,根是全局对象)。垃圾回收器将定期从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和收集所有不能获得的对象。
这个算法比前一个要好,因为“有零引用的对象”总是不可获得的,但是相反却不一定,参考“循环引用”。
3、标记-整理算法
标记-清除算法清理完后,保留的对象在内存中的位置不会移动,会出现内存碎片,新增对象时需要找到合适的内存位置进行存放。标记-整理算法可以在清除无用对象后整理剩余对象改动对象在内存中的位置,方便后续对象存入
节流和防抖
节流:一段时间内执行一次
if(flag) return;
防抖:一段时间内执行一次,再次触发会重新计时
clearTimeout(timeout);
1、防抖(debounce)多次点击只执行最后一次
第一次点击时生成timer,随后在限制时间内再次点击会清空timer重新生成timer
clearTimeout(timer);
timer = setTimeout(function(){
...
}, delay)
2、节流(throttle)一段时间内执行一次
定义一个flag=true标记,点击时改为false,当一段时间后改为true
let falg = true
let thtottle = function(delay) {
if(flag) {
flag = false
setTimeOut(function() {
flag = true
...
},delay)
}
}
js中的EventLoop(事件循环)
原文理解 JS 中的 EventLoop 事件循环(详细图文教程)
开始,任务先进入 Call Stack
同步任务直接在栈中等待被执行,异步任务从 Call Stack 移入到 Event Table 注册
当对应的事件触发(或延迟到指定时间),Event Table 会将事件回调函数移入 Event Queue 等待
当 Call Stack 中没有任务,就从 Event Queue 中拿出一个任务放入 Call Stack
而 Event Loop 指的就是这一整个圈圈:
它不停检查 Call Stack 中是否有任务(也叫栈帧)需要执行,如果没有,就检查 Event Queue,从中弹出一个任务,放入 Call Stack 中,如此往复循环。
js中数组方法
1、concat(arr1,arr2) 连接两个或更多的数组,并返回结果。
2、filter() 检测数值元素,并返回符合条件所有元素的 新数组 。
let ages = [11,22,14,25,31,2,8]
ages.filter((item) => {
return item > 20
})
console.log(ages);//[22,25,31]
3、find() 返回符合传入测试(函数)条件的数组元素。
let ages = [3,10,18,20]
ages.find((item) => {
return item>10
})
console.log(ages);//[18]
4、findIndex() 返回符合传入测试(函数)条件的数组元素索引。
5、forEach(currentValue, index, arr) 数组每个元素都执行一次回调函数。
6、includes() 判断一个数组是否包含一个指定的值。
7、indexOf() 搜索数组中的元素,并返回它所在的位置。
8、join() 把数组的所有元素放入一个字符串。
9、pop() 删除数组的最后一个元素并返回删除的元素。
10、shift() 删除并返回数组的第一个元素。
11、splice(index,howmany,item1,…,itemX) 从数组中添加或删除元素。
影响原数组
let fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.splice(2,0,"Lemon","Kiwi");
console.log(fruits);//Banana,Orange,Lemon,Kiwi,Apple,Mango
let numbers = [1,2,3,4,5,6]
numbers.splice(2,1);
console.log(numbers);//[1,2,4,5,6]
12、slice(start, end) 选取数组的一部分,并返回一个新数组。
不影响原数组
let fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
let citrus = fruits.slice(1,3);
console.log(citrus);//Orange,Lemon
13、map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
14、reduce() 接收一个函数(必须)和一个初始值(可选),该函数接收两个参数:
let arr = [1,2,3,4]
arr.reduce((a,b) =>a+b);//10
arr.reducec((a,b) => a+b , 1)//11 指定初始值
查看详情点击此处 https://www.runoob.com/jsref/jsref-obj-array.html
js中的原型链
1、所有的引用类型(数组、函数、对象)可以自由扩展(除null以外)。
2、引用类型都有'--proto--'属性(隐式原型,理解为浏览器使用)
3、引用类型都有'prototype'属性(显示原型,理解为程序员使用)
4、所有引用类型的'--proto--'指向它的构造函数的'prototype'属性
5、当试图得到一个对象的属性时,如果这个对象本身不存在这个属性,
那么就会去它的’_ _ proto_ _'属性(也就是它的构造函数的’prototype’属性)中去寻找。
(一直向上一级(构造函数)直到Object(object的__proto__指向null)形成原型链)
js中的继承
可以通过原型来实现继承
function Person(name,age) {
xxx;
}
function Student(score) {
xxx;
}
//改变学生的原型的指向即可让学生继承人
//Student 继承 Person
Student.prototype = new Person('xx',10);
JS中的宿主对象与原生对象
js宿主对象:alert(); 弹出提示框
confirm(); 显示带有一段消息以及确认按钮和取消按钮的对话框
prompt(); 显示可提示用户输入的对话框setInterval();clearInterval();setTimeout(); clearTimeout();
window、document
抄自js宿主对象
js内置对象: Object、Array、Math、Number、String、Date、JSON
抄自JS的对象与内置对象详细介绍
事件冒泡与事件捕获
抄自事件冒泡与事件捕获
事件捕获:向下
dom.addEventListener("click", function(e){
}, true)
事件冒泡:向上
dom.addEventListener("click", function(e)
})
typeof 和 instanceof
typeof原理:不同的对象在底层都表示为二进制,在Javascript中二进制低三位存储其类型信息。
000: 对象
001: 整数
010: 浮点数
100:字符串
110: 布尔
typeof返回字符串
typeof ''; // string 有效
typeof 1; // number 有效
typeof NaN; // “number”
typeof true; // boolean 有效
typeof undefined; // undefined 有效
typeof null; // object 无效
typeof [] ; // object 无效
typeof new Function(); // function 有效
typeof new Date(); // object 无效
typeof new RegExp(); // object 无效
instanceof返回boolean类型
Instanceof 是通过原型链去查找,找到某个对象的原型对象,使用原型对象的constructor找到构造函数,看看构造函数与Instanceof后面的是否相同,不相同,继续向上查找,直到尽头,找到为True,没找到为false。
原文instanceof测字符串为啥是false
'123' instanceof String; // false
let str = new String('123');
str instanceof String;// true
123 instanceof Number; // false
let num = new Number(123);
num instanceof Number;//true
[] instanceof Array; // true
[] instanceof Object; // true
[] instanceof RegExp; // false
new Date instanceof Date; // true
toString
1、返回字符串
2、类型检测
toString.call([1,2]) //"[object Array]"
toString.call(function name(){}) //"[object Function]"
toString.call('1') //"[object String]"
对象.toString()// "[object Object]"
3、进制转换
let num = 10;
num.toString(2);
99..toString(2);//直接用数字要两个点
0.1 + 0.2 !== 0.3
在JavaScript中的二进制的浮点数0.1和0.2并不是十分精确,在他们相加的结果并非正好等于0.3,而是一个比较接近的数字 0.30000000000000004
1、toFixed(2) 保留两位小数
2、( 1 + 2 ) / 10
3、Number.EPSILON(并且无限接近0,但不等于0)
( 0.1 + 0.2 - 0.3 ) > Number.EPSILON
promise
原文Promise不会??看这里!!!史上最通俗易懂的Promise!!!
Promise的返回值还是Promise
解决回调地狱问题,是代码易维护;共有三种状态
pending: 初始状态,成功或失败状态。
fulfilled: 意味着操作成功完成。
rejected: 意味着操作失败。
let promise = new Promise(function(resolve,reject){
if(falg) {
resolve('成功!');
}else {
reject('失败');
}
})
let promise1 = new Promise(function(resolve,reject){
resolve('promise1');
})
//then接受成功和失败两个回调函数
promise.then(
function(data){
console.log(data);//成功!
},function(reason){
consol.log(reason);//失败!
}
).catch(function(error){
consol.log(error);//失败!
})
//all方法,全部成功走成功,一个失败走失败
let promiseAll = Promise.all([promise,promise1]).then((data) => {
console.log(data);//['成功!','promise1'];
})
//race方法,执行最快的,列设置timeout
并发请求
async function sequentialWait() {
console.log("== sequentialWait 开始 ==");
// 1. 启动两个计时器,互不等待
const slow = resolveAfter2Seconds();
const fast = resolveAfter1Second();
// 2. 等待较慢的计时器完成后,打印结果
console.log(await slow);
// 3. 等待较快的计时器完成后,打印结果
console.log(await fast);
console.log("== sequentialWait 结束 ==");
}
async和await
async await内部是同步的,await等待一个promise返回
await命令后面的Promise对象的运行结果可能是rejected,所以最好把await命令放在try...catch代码块中
Map集合和Set集合和Object
const myMap = new Map();
myMap.set('key1', 'value1');
myMap.set('key2', 'value2');
myMap.get('key2');
for (const [key, value] of myMap) {
console.log(key, value);
}
const mySet = new Set();
mySet.add('name','xxx');
for (let value of mySet) {
console.log(value);
}
let object = {a:'123',b:'456'};
for (let value of Object.entries(object)) {
console.log(value);// ['a', '123'] ['b', '456']
}
几种for循环的使用
1、常规for 遍历数组
for(let i = 0; i < array.length; i++){
...
}
2、for in 遍历对象
let obj = {name: 'lanbo',value:20}
for(let key in obj){
console.log(key);//key为对象的key
}
for…in… 方法在遍历过程中会访问原型上的所有属性,如果扩展了js原生的Array类,则会影响遍历结果
建议不要用for in遍历数组( 我们无法保证我们引入的js是否会采用prototype扩展原生的Array )
Object.prototype.say = 'lanbo';
const array = [1,2,3,4,5,6,7,8,9];
for(let i in array){
console.log(array[i]);
}
//执行结果为:
1 2 3 4 5 6 7 8 9 lanbo
3、forEach (针对可迭代对象(Array, Map, Set, arguments等)
let arr = [1, 2, 3, 4, 5, 6, 7];
arr.forEach(function (value, index, obj) {
console.log(value, index, obj);
//value:当前值 index:当前值得索引 obj:正在遍历的数组
});
4、for of 可以迭代数组、类数组以及任何可以迭代的对象(maps、sets、DOM集合)
1、遍历数组,支持解构
let myArray = [{code: 1}, {code: 2}];
for (let value of myArray) {
console.log(value.code);
}
//解构
for (let {code}of myArray) {
console.log(code);
}
2、遍历字符串
const message = 'jsscript';
for (const character of message) {
console.log(character);
}
3、支持Map和Set的遍历
// Map 和 Set 迭代
const myMap = new Map();
myMap.set('key1', 'value1');
myMap.set('key2', 'value2');
for (const [key, value] of myMap) {
console.log(key, value);
}
const keys = new Set(['key1', 'key2', 'key3', 'key4']);
for (let value of keys) {
console.log(value);
}
4、普通对象 (需要Object.entries将对象转成键和值的元组数组)
const people = { name:'lanbo', value: 20 };
for (const [prop, value] of Object.entries(people)) {
//Object.entries将对象转成键和值的元组数组[[''name','lanbo'],['value',20]]
console.log(prop, value);
}
总结:
for in 和 for of 的区别
1、for in 遍历出来的是属性key,for of可以遍历出prop和value
2、for in 拿值用 obj[key] , for of 拿值用 obj.key
3、for in 只能遍历可枚举属性 for of ( Array,Map,Set,String,TypedArray,arguments 对象等等)
ES6新特性
原文ES6新特性
let和const命令
模板字符串
解构表达式 const { } = Object; const [ ] = Array;
函数箭头
数组方法Map和Reduce
运算符扩展
Promise
Set和Map
class(类)的基本语法
Generator函数
for…of循环
ES6中Module和CommonJS模块
原文聊聊CommonJS与ES6 Module的使用与区别
CommonJs
A.js
function show() {}
//第一种
module.exports = {show};
//第二种
exports.show = show; //第一第二是等价的 exports 指向 module.exports
B.js
let fileName = 'A.js';
const AModule = require('./' + fileName);//支持表达式,动态
Module
A.js
//第一种
export function show(){}
//第二种
function show() {};
export {show}; //两种是等价的
//默认导出
export default show;
B.js
import {show} from './A.js';
//修改名称
import {show as newName } from './A.js';
//全部导入
import * as AModule from './A.js';
AModule.show();
区别:
1、对于模块的依赖,CommonJs是动态的(require可以写表达式),Module是静态的;
2、CommonJs是拷贝源文件值(源文件改变值时,新文件不改变),Module是拷贝源文件引用地址(源文件改变,新文件也改变)
js中的设计模式
原文JS中常用的设计模式
工厂模式:
批量制造对象,但instanof为false;
构造函数:
批量制造对象,instanceof为true
单例模式:
保证一个类只有一个实例
观察者模式(发布-订阅)
//发布者
class Sales {
constructor(){
this.clientList = [];
}
listen(fn){
this.clientList.push(fn);
}
//发布函数(发布售楼消息)
trigger(){
//遍历花名册,给他们发消息
for(var i=0;i<this.clientList.length;i++){
var fn = this.clientList[i];
fn.apply(this,arguments);
}
}
}
let sales = new Sales();
//订阅
sales.lisen(fn1);
sales.lisen(fn2);
//发布
sales.trigger();