JS 的主要特点:
bai1. 语法类似于常见的高级du语言,如 C 和 Java;
2. 脚本语言,不需要编译就可以由解释器直接运行;
3. 变量松散定义,属于弱类型语言;
4. 面向对象的。
面向对象的三大特性:
链接
一、对象
深拷贝和浅拷贝
4.1 浅拷贝
1.Object.assign()
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
缺点:
自身可枚举属性和Symbol属性,无原型和不可枚举属性
let copyObj = Object.assign({}, sourceObj);
2. 展开运算符浅拷贝
let copyObj = {...target};
4.2 深拷贝
1. JSON.parse(JSON.stringify(obj))
缺点:undefined、function、symbol 会在转换过程中被忽略。。。
2. 通过递归的方法
对每一层创建对象,对象赋值
function deepClone(source){
const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
for(let keys in source){ // 遍历目标
if(source.hasOwnProperty(keys)){
if(source[keys] && typeof source[keys] === 'object'){ // 如果值是对象,就递归一下
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
}else{ // 如果不是,就直接赋值
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
一、数组
### 0.数组方法总结
arr.slice
arr.slice(start, end)
arr.slice(2,4)
arr.slice()
arr.concat()
var a = [1,2,3];
let b = a.concat(4,5);
arr.concat(arr2,arr3);
arr.concat()
arr.splice()
arr.splice(index, howmany, item1, ..., itemX )
arr.splice(2,0,"William")
1.复制数组的几种方式
1.1浅拷贝
- arr.slice()
let a = [1,2,3,{a:1}];
let b = a.slice();
a[3].a=2
console.log(b)//[1,2,3{a:1}]
- arr.concat()
let a = [1,2,3,{a:1}];
let b = a.concat();
a[3].a=2
console.log(b)//[1,2,3,{a:1}]
1.2深拷贝
- 对象序列化实现
JSON.parse(JSON.stringjfy(a))
var a = {v1:1, v2:2};
var b = JSON.parse(JSON.stringify(a));
b.v1 = 3;
console.log("对象a:",a);
console.log("对象b:",b);
- 递归遍历
function clone(obj){
let copy = {};
for(let attr in obj ){
copy[attr] = typeof(obj[attr])==="object" ? clone(obj[attr]) : obj[attr];
}
return copy;
}
2.找出数组arr中重复出现的元素
function duplicates(arr) {
let ret = [];
arr.forEach((val)=>{
if(arr.indexOf(val)!==arr.lastIndexOf(val)&&ret.indexOf(val)==-1)
ret.push(val)})
return ret
}
3. 数组去重
- reduce+includes
//1.reduce+includes
let arr = [1,2,3,4,4,1]
function unique(arr){
return arr.reduce((prev,cur)=>{
return prev.includes(cur)? prev : [...prev,cur]
},[])
}
console.log(unique(arr));
//法一、es5 Array.from+new Set()
// function unique(arr){
// return Array.from(new Set(arr))
// }
// console.log(unique(arr1));
//法二、es6 删掉重复的 双层for+splice删掉
// function unique(arr){
// for(let i=0;i<arr.length;i++){
// for(let j = i+1;j<arr.length;j++){
// if(arr[i]==arr[j]){
// arr.splice(j,1);
// j--;
// }
// }
// }
// return arr;
// }
// console.log(unique(arr1));
//法三、 new [] includes()和indexOf()
// function unique(arr){
// let ret = [];
// for(let i of arr){
// if(ret.indexOf(i)===-1){
// ret.push(i)
// }
// }
// return ret;
// }
// console.log(unique(arr1));
//法四、 object方法 new {}
function unique(arr){
let ret = [];
let obj = {};
for(let i of arr){
if(!obj[i]){
ret.push(i);
obj[i] = 1;
}
}
return ret;
}
let arr1 = [1,2,3,44,44,5];
console.log(unique(arr1));
4. 数组扁平化
最简法 arr.flat(Infinity)
- 如果数组的元素都是数字,那么我们可以考虑使用 toString 方法
function flatten(arr){
return arr.toString().split(",").map((val)=>+val)
}
console.log(flatten(arr));
- 递归法
//法一、递归
//1.声明数组
//2. 遍历
//3.if元素是数组,新数组concat扁平化元素数组
//4.不是数组,新数组push元素
//返回新数组
// function flatten(arr){
// let ret = [];
// for(let i = 0;i<arr.length;i++){
// if(Array.isArray(arr[i])){
// ret = ret.concat(flatten(arr[i]))
// }else {
// ret.push(arr[i])
// }
// }
// return ret
// }
- reduce法+concat
//法二 reduce+concat()方法
// function flatten(arr){
// return arr.reduce((prev,cur)=>{
// return Array.isArray(cur)? prev.concat(flatten(cur)): [...prev,cur]
// },[])
// }
- while法+…展开运算符
// function flatten(arr){
// while(arr.some(val=>Array.isArray(val))){
// arr = [].concat(...arr)
// }
// return arr
// }
二、继承
0.prototype、proto constructor
1.原型链:
访问一个对象的属性时,如果对象内部找不到
属性–>proto(父对象)–> (爷爷对象)–> null;
2.prototype
3.constructor
1.原型链继承
链接
Son.prototype = new Father()
var son = new Son()
son instanceof Son
son instanceof Father
特点:
1.简单
2.访问父类新增的原型属性和方法
缺点:
1.无法实现多继承
2.创建子类实例时,无法向父类构造函数传参
3.来自原型对象的引用属性是所有实例共享的
2.构造继承
修改父类构造函数this实现的继承。
我们在子类构造函数中执行父类构造函数,同时修改父类构造函数的this为子类的this。
function Parent() {
this.name = ['fedaily']
}
function Child() {
Parent.call(this)
}
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
- 优点:
1.传参
2.解决了引用类型共享的问题
3.可以多继承 - 缺点:
1.实例并不是父类的实例,只是子类的实例
2.只能继承父类的实例属性和方法,不能继承原型属性/方法
3.所有方法都定义在构造函数中,每次都需要重新创建(对比原型链继承的方式,方法直接写在原型上,子类创建时不需要重新创建方法)
3.组合继承
同时结合原型链继承、构造函数继承就是组合继承了。
function Parent() {
this.name = 'fedaily'
}
Parent.prototype.getName = function() {
return this.name
}
function Child() {
Parent.call(this)
this.topic = 'fe'
}
Child.prototype = new Parent()
// 需要重新设置子类的constructor,Child.prototype = new Parent()相当于子类的原型对象完全被覆盖了
Child.prototype.constructor = Child
- 优点
- 同时解决了构造函数引用类型的问题,
- 同时避免了方法会被创建多次的问题
- 缺点
- 父类构造函数被调用了两次。
- 同时子类实例以及子类原型对象上都会存在name属性。虽然根据原型链机制,并不会访问到原型对象上的同名属性,但总归是不美。
4.寄生组合继承
在组合继承的基础上,解决了父类构造函数调用两次的问题。我们来看下如何解决的:
function Parent() {
this.name = 'fedaily'
}
Parent.prototype.getName = function() {
return this.name
}
function Child() {
Parent.call(this)
this.topic = 'fe'
}
// 仔细看这个函数的实现
inherit(Child, Parent)
function inherit(child, parent) {
var prototype = object(parent.prototype)
prototype.constructor = child
child.prototype = prototype
}
// 这个函数的作用可以理解为复制了一份父类的原型对象
// 如果直接将子类的原型对象赋值为父类原型对象
// 那么修改子类原型对象其实就相当于修改了父类的原型对象
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
-
优点
这种方式就解决了组合继承中的构造函数调用两次,构造函数引用类型共享,以及原型对象上存在多余属性的问题。是推荐的最合理实现方式(排除ES6的class extends继承哈哈哈)。 -
缺点
没有啥特别的缺点
5.ES6继承
ES6提供了class语法糖,同时提供了extends用于实现类的继承。这也是项目开发中推荐使用的方式。
使用class继承很简单,也很直观:
class Parent {
constructor() {
this.name = 'fedaily'
}
getName() {
return this.name
}
}
class Child extends Parent {
constructor() {
// 这里很重要,如果在this.topic = 'fe'后面调用,会导致this为undefined,具体原因可以详细了解ES6的class相关内容,这里不展开说明
super()
this.topic = 'fe'
}
}
const child = new Child()
child.getName() // fedaily
三、数据结构与算法
1.排序之快排
//参照物+划分+递归
var quickSort = function(arr){
if(arr.length<=1){return arr}
let pivotIndex = arr.length >> 1;
let pivot = arr[pivotIndex];
arr.splice(pivotIndex,1);
let left = [];
let right = [];
for(let i = 0;i<arr.length;i++){
if(arr[i]<pivot){left.push(arr[i])
}else{
right.push(arr[i])
}
}
return quickSort(left).concat([pivot],quickSort(right));
}
let arr = [3, 2, 4, 5, 1];
console.log(quickSort(arr));
2.排序之选择排序
function selectionSort(arr) {
var len = arr.length;
var minIndex, temp;
for (var i = 0; i < len - 1; i++) {
minIndex = i;
for (var j = i + 1; j < len; j++) {
if (arr[j] < arr[minIndex]) { //寻找最小的数
minIndex = j; //将最小数的索引保存
}
}
temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
return arr;
}
3.排序之插入排序
function insertionSort(arr) {
var len = arr.length;
var preIndex, current;
for (var i = 1; i < len; i++) {
preIndex = i - 1;
current = arr[i];
while(preIndex >= 0 && arr[preIndex] > current) {
arr[preIndex+1] = arr[preIndex];
preIndex--;
}
arr[preIndex+1] = current;
}
return arr;
}
1.栈
- q1: 十进制转二进制
function dec2bin(decNumber){
let stack = [];
let remainder;
while(decNumber>0){
remainder = decNumber%2;
decNumber = Math.floor(decNumber/2);
stack.push(remainder);
}
let str = '';
while(stack.length>0){
str+=stack.pop();
}
return str;
}
let decn = 12;
console.log(dec2bin(decn));
2.队列
优先级队列
优先级队列, 在插入一个元素的时候会考虑该数据的优先级。(和其他数据优先级进行比较)
比较完成后, 可以得出这个元素正确的队列中的位置。 其他处理方式, 和队列的处理方式一样。
也就是说, 如果我们要实现优先级队列, 最主要是要修改添加方法. (当然, 还需要以某种方式来保存元素的优先级)
1. 击鼓传花的规则:
几个朋友一起玩一个游戏, 围成一圈, 开始数数, 数到某个数字的人自动淘汰。最后剩下的这个人会获得胜利, 请问最后剩下的是原来在哪一个位置上的人?
动态规划
1.剪绳子
题目:
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1
示例 2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
提示:
2 <= n <= 58
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
//动态规划
var cuttingRope = function(n) {
let dp = [0,0,1];
let tmp;
for(let i = 3;i<=n;i++){
dp[i] = 0;
// dp[i]=2*(i-2);
for(let j = 1;j<=Math.floor(i/2);j++){
tmp = Math.max(j*(i-j),j*dp[i-j],dp[j]*(i-j),dp[j]*dp[i-j]);
dp[i]<tmp&&(dp[i]=tmp);
}
}
return dp[n]
};
剪绳子2
2 <= n <= 1000
/**
* @param {number} n
* @return {number}
*/
//贪心
var cuttingRope = function(n) {
if(n==2)return 1;
if(n==3)return 2;
if(n==4)return 4;
let res = 1;
while(n>4){
res = res*3;
res = res%(1e9+7);
n -= 3;
};
return res*n%(1e9+7);
};
2. 二进制中1的个数
法一、n&(n-1)
var hammingWeight = function(n) {
let res = 0;
while(n){
n = n & (n-1);
res++;
};
return res;
};
法二、n&1==1 n = n>>>1;
事件循环
- 首先将执行栈中代码同步执行,将这些代码中异步任务加入后台线程中
- 执行栈中的同步代码执行完毕后,执行栈清空,并开始扫描微队列
- 取出微队列队首任务,放入执行栈中执行,此时微队列是进行了出队操作
- 当执行栈执行完成后,继续出队微队列任务并执行,直到微队列任务全部执行完毕
- 最后一个微队列任务出队并进入执行栈后微队列中任务为空,当执行栈任务完成后,开始扫面微队列为空,继续扫描宏队列任务,宏队列出队,放入执行栈中执行,执行完毕后继续扫描微队列为空则扫描宏队列,出队执行
- 不断往复…
promise
状态:
pending\fufilled\rejected