随着前端工作年限的增长,逐渐意识到基础(语言基础、数据结构与算法基础、众多计算机理论基础)的重要性。所以开始梳理一下基础知识吧
一些用于解决实际业务问题的编程手段都属于基础,深入了解各种语言特性,烂熟于心的使用语言,更是整个编程过程中基础的基础
1. 数据类型
*组成:
- 原始类型 string number boolean undefined null symbol 栈内存
- 引用类型 数组 函数 对象 堆内存 通过栈内存中指针指向堆内存地址
*判断类型
-
- typeof
typeof对于原始类型,除了null都可以显示正确类型
typeof对于引用类型除了函数,都返回 ‘object’ 函数: ‘function’
-
- instanceof
Object.prototype.toString.call(type)
-
- 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]”
*类型转换
待转换类型 | 转换目标 | 结果 |
---|---|---|
number | 字符串 | 5=>’5′ |
boolean | 字符串 | ‘true’ ‘false’ |
undefined | 字符串 | ‘undefined’ |
null | 字符串 | ‘null’ |
Symbol(‘ss’) | 字符串 | ‘Symbol(‘ss’)’ |
Array | 字符串 | [1,2] => ‘1,2’ |
对象 | 字符串 | ‘[object Object]’ |
string | 数字 | ‘1’=>1 |
数组 | 数字 | []=>0 [1]=>1 [2]=> 其他=> NaN |
null | 数字 | 0 |
undefined | 数字 | NaN |
除了数组的引用数据类型 | 数字 | NaN |
string | 布尔值 | 非空字符串都为true |
number | 布尔值 | 除了0、-0、NaN都为true |
undefined null | 布尔值 | false |
引用类型 | 布尔值 | true |
console.log(1 + '1' ) // 11
console.log(true + 0) //1
console.log({}+[]) // "[object Object]"
console.log(4 + {} ) // "4[object Object]"
console.log(4 + [1] ) // "41"
console.log('a' + + 'b') // "aNaNb"
console.log ( [] == 0 ) // true []根据右边值类型转化为数字 []=0 所以 0==0
console.log ( ! [] == 0 ) // true ![]根据右边值类型转化为数字 []为对象转化为布尔值时为真,所以![]=!true=false=0=0
console.log ( [] == ! [] ) //true 比较的是引用, 每次比较时相当于开辟新栈引用
console.log ( [] == [] ) //false 比较的是引用, 每次比较时相当于开辟新栈引用
console.log({} == !{}) //false
console.log({} == {}) //false
复制代码
2. this的指向
*this总是指向直接调用它的对象
1.new绑定 :由new调用 绑定到新创建的对象
2.显示绑定:由call 或apply bind调用,绑定到指定的对象
3.隐式绑定:由上下文对象调用,绑定到那个上下文对象 ( 对象对函数的引用 注意仅仅只是引用,调用时“拥有”)
4.默认绑定:在严格模式下绑定到undefined,否则绑定到全局对象
例外:1.箭头函数并不会使用四条标准规则,根据当前词法作用域来决定this 即包裹箭头函数的第一个普通函数的this
2.显示绑定中指向空对象 创建DMZ对象 var Ø = Object.create(null)
this常考难题:
function Pet(name){
this.name = name
this.getName=()=>this.name
}
const cat = new Pet('Dog')
console.log(cat.getName());
const {getName} = cat
console.log(getName());
//答案: 'Dog' 'Dog 箭头函数中this是由定义时决定的,而不是使用时,也就是指向其所在的词法作用域
-------------------------------------------------------------------------------------
const obj = {
message:'heelo wordkl',
logMessage(){
console.log(this.message)
}
}
// undefined
// setTimeout(obj.logMessage,1000)' setTimeout中任将obj.logMessage当做普通函数来调用,故this指向window
-------------------------------------------------------------------------------------
const obj = {
who:'world',
greet(){
console.log(this);
return `hello ${this.who}`
},
well:()=>{
console.log(this);
return `goobye ${this.who}`}
}
console.log(obj.greet());
console.log(obj.well());
// 答案: hello world 和 undefined
可能有同学就会疑惑了,怎么这里的箭头函数和上面的箭头函数this指向又变了,明明都被包裹在里面吗,不是说箭头函数this的指向由其所在词法作用域(外部作用域)决定吗?
是的,没错, 区别关键点是严格明确箭头函数的在哪里被定义?
在第一个问题中,箭头函数是被this.getName声明在构造函数里的,是被定义 , 而在对象well中只是被引用,不算定义,所以obj.well()还是指向单纯的箭头函数,故this指向window
-------------------------------------------------------------------------------------
棘手的length
var length = 4
function callback(){
console.log(this.length);
}
const obj = {
length: 5,
method(callback){
callback();
}
}
obj.method(callback(),1,2)
//答案 4 callback在 obj.method中只是作为函数被调用,所以this指向全局对象
-------------------------------------------------------------------------------------
调用参数
var length = 4
function callback(){
console.log(this.length);
}
const obj = {
length: 5,
method(){
arguments[0]()
}
}
obj.method(callback,1,2)
答案: 3 callback作为 obj.method的参数被调用,被obj.method调用,故this指向obj.method, 所以length为此函数的参数长度
复制代码
3. == 和 ===有什么区别?
*== 如果对比双方类型不一样的话,就会自动进行类型转换
=== 判断两者类型和值是否相同
4. 闭包
*当函数可以记住并访问所在的词法作用域,即使函数在当前词法作用域之外执行,这时就产生了闭包
利用闭包解决经典问题:
闭包=函数+函数能够访问的自由变量
经典面试题中,循环中使用闭包解决 ‘var’ 的问题
for(var i = 0; i<=5;i++){
setTimeout(function(){
console.log(i)
},i*1000)
}
三种方式:
1. 立即执行函数创建闭包
for(var i = 0; i<=5;i++){
(function(j){
setTimeout(function(){
console.log(j)
},i*1000)
})(i)
}
2. for(var i = 0; i<=5;i++){
setTimeout(function(){
console.log(i)
},i*1000,i)
}
3. let
for(let i =1;i<=5;i++){
setTimeout(function(){
console.log(i)
},i*1000)
}
复制代码
5. 浅拷贝与深拷贝
浅拷贝: 对被拷贝对象的基本类型值进行拷贝,对所属子对象的地址进行拷贝
深拷贝:对被拷贝对象的基本类型值和所属子对象的值全拷贝
浅拷贝实现方式:
// 浅拷贝
实现方式: for循环 object.assign() slice() concat() es6扩展运算符 Array.from(numbers)
let copy = {
a :1,
jobs:{
type:'FE'
}
}
let b = Object.assign({},copy) // Object.assign
let b = {...copy} // ... 展开操作符
function clone(target) {
if(target === null ) {
return target
}
// 克隆 数组 和 对象
let cloneTarget = Array.isArray(target) ? [] : {}
for (const key in target) {
if (target.hasOwnProperty(key)) {
cloneTarget[key] = target[key]
}
}
return cloneTarget
}
//深拷贝
方法一: let deepCloneObj = JSON.parse(JSON.stringify(a)
局限性:
- 会忽略undefined
- 会忽略symbol
- 不能序列化函数
- 不能解决循环引用对象
方法二: 递归调用
function deepClone(obj,cache= new WeakMap()){
if(!(obj instanceof Object)) return obj // 类型判断
if(cache.get(obj)) return cache.get(obj) //防止循环引用
if(obj instanceof Function){ //支持函数
return function(){
return obj.apply(this,arguments);
}
}
if(obj instanceof Date) return new Date(obj) //支持date对象
if(obj instanceof RegExp) return new RegExp(obj.source,obj.flags)
// 还可以增加其他对象的类型判断,比如Map Set等
const res = Array.isArray(obj)?[]:{}
cache.set(obj,res)
Object.keys(obj).forEach((key) =>{
if(obj[key] instanceof Object){
res[key] = deepClone(obj[key])
}else{
res[key] = obj[key]
}
})
return res
}
复制代码
6.模拟实现new
new创建实例 new做了什么?
- 创建一个对象,并继承其构造函数的prototype
- 执行构造函数,函数内部的this指向被指定为该新实例
- 返回新实例
7.原型链
如何回答原型链:
JS管理对象关系的内部机制
说到原型链,就要理清原型、实例、构造函数三者的关系
每个构造函数都有一个原型对象,构造函数通过prototype指向原型,原型通过contructor指向构造函数,, 实例通过__proto__指向原型
如果原型是另一个类型的实例,那这个原型内部就有一个指针指向另一个原型,这样一级一级的向上查询或向下拷贝就构成了原型链
具体来说
1.在访问对象的某个成员的时候会先在对象中找是否存在
2.如果当前对象中没有就在构造函数的原型对象中找
3.如果原型对象中没有找到就到原型对象的原型上找
4.直到Object的原型对象的原型是null为止
复制代码
8.原型继承
一. 组合继承
原型链继承的原理很简单,直接让子类的原型对象指向父类实例,当子类实例找不到对应的属性和方法时,就会往它的原型对象,也就是父类实例上找,从而实现对父类的属性和方法的继承
function Parent(){
this.name= "tmd 烦死了"
}
Parent.prototype.getName = function(){
return this.name
}
function Child(){}
Child.prototype = new Parent()
Child.prototype.contructor = Child
const child1 = new Child()
const child2= new Child()
二. 构造函数继承
构造函数继承,即在子类的构造函数中执行父类的构造函数,并为其绑定子类的this,让父类的构造函数把成员属性和方法都挂到子类的this上去,这样既能避免实例之间共享一个原型实例,又能向父类构造方法传参
function Child() {
Parent.call(this, 'zhangsan')
}
三. 组合式继承
function Child() {
// 构造函数继承
Parent.call(this, 'zhangsan')
}
//原型链继承
Child.prototype = new Parent()
Child.prototype.constructor = Child
四、寄生组合式继承
function Child() {
// 构造函数继承
Parent.call(this, 'zhangsan')
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
五、es6 class继承
class Parent2{
constructor(value){
this.val = value
}
getValue(){
console.log(this.val);
}
}
class Child4 extends Parent2{
constructor(value){
super(value)
}
}
复制代码
9. var、let、const的区别
- 什么是提升?
var声明会被拿到函数或全局作用域顶部,位于作用域中所以的代码之前,这个现象就叫做“提升”
- 什么是暂时性死区?
es6规定,如果在区块中存在let/const声明,这个区块对这些声明的变量,一开始就形成封闭的作用域,凡是在声明之前使用这些变量,就会报错
函数提升优先于变量提升,函数提升会把整个函数挪到作用域顶部,变量提升只会把声明挪到顶部
1.var存在提升, let/const不存在
2.var在非严格模式下声明变量会挂载到全局window上,let/const不会
3.let和const声明形成块级作用域
4. 同一作用域下,let/const不能声明同名变量,而var可以
复制代码
10.模块化
- 如何理解模块化?
模块化就是把项目中通用的或联系紧密的一组属性和方法抽象提取出来,以便项目中其他地方复用或项目成员复用,降低代码维护成本
- 模块化的好处:
解决命名冲突
提供复用性
提高代码可维护性
- 实现方式:
- 立即执行函数
- AMD 和CMD
AMD
define(['./a','./b'],function(a,b){
a.do()
b.do()
}
CMD
define(function(require,exports,module)){
var a = require('./a')
a.doSomething()
}
复制代码
- CommonJS
目前多在nodejs中使用
module.exports = {a:1}
exports.a =1
var module = require('./a.js)
复制代码
- ES Module
es module 是原生实现的模块化方案
import xxx from './a.js'
import {xxx} from './a.js'
import * as xxx from './a.js'
export function a(){}
export default function(){}
export {}
export default {}
复制代码
- esm 与commJS的差异
CommonJS支持动态导入 esm不支持
CommonJS是同步导入 esm是异步导入
CommonJS导出的是值拷贝 esm导入导出值都指向同一个内存地址
esm会编译成require/exports来执行
10. 数组方法大全
1. 创建数组
1. 字面量方法
var arr4 = []
2. 构造函数 const arr1 = new Array(20)
3. Array.of() 将一组数值转换成数组,而不考虑参数的数量或类型 es6新增 了弥补数组构造函数Array()的的不足
4. Array.from()
1.将非数组对象转换成真正的数组
2.映射转换
function(...args){
return Array.from(args,value=>value+1)
}
3.生成1~100数组
Array.from({length},(_,index)=>index)
4. 数组去重 Array.from(new Set(array))
复制代码
2. 数组类型判断
Array.isArray(arg)
Object.prototype.toString.call(obj).slice(8,-1)==='Array'
复制代码
3. 数组方法
- join() 用指定分隔符将数组每一项拼接为字符串
- 栈方法
push() 向数组末尾添加新元素 返回数组长度
unshift() 向数组前面添加新元素 返回数组长度
pop() 删除数组的最后一项 返回删除项
shift 删除数组的第一项 返回删除项
复制代码
- 排序方法
reverse() 反转数组项的顺序
sort() 默认升序
复制代码
- 操作方法
arr1.concat(arr2) 合并数组
slice(start,end) 基于当前数组创建新数组
splice(start,插入个数,替换值) 返回被删除的数组 如果没有被删除数组 则返回 []
fill(填充值,strart,end)
复制代码
- 查找方法
indexof(1)
lastIndexOf(2)
find 找出第一个符合条件的数组元素
findIndex 返回第一个符合条件的数组元素的index
复制代码
- 迭代方法
every 对数组每一项都运行给定的函数 每一项都返回true return true
some 对数组每一项都运行给定的函数,若有一项返回true return true
filter 对数组每一项都运行给定的函数 返回结果为true的数组
map 对数组每一项都运行给定的函数, 返回用返回结果组成的一个新数组
forEach
reduce 比上面的五个迭代方法都一个参数 :上一项的返回值
reduceRight 比上面的五个迭代方法都一个参数 :上一项的返回值
entries() keys() values()
includes 判断数组中是否存在元素
复制代码
4. 摊平多维数组多种姿势
let array =[[1,2],[1,3,4],[4,{a:1}],['jack',false]]
const str = JSON.stringify(array)
1. ES6 array.flat()
2.replace+split:
const flatSplit = str.replace(/(\[|\])/g,'').split(',')
console.log('flatSplit: ', flatSplit);
3.relace+JSON.parse
const flatParse = str.replace(/(\[|\])/g,'')
const str2 = '['+flatParse+']'
console.log(JSON.parse(str2));
4.普通递归:
let newArr = []
function flatRecursion(arr){
for(let i=0;i<arr.length;i++){
let item = arr[i]
if(Array.isArray(arr[i])){
flatRecursion(item)
}else{
newArr.push(item)
}
}
}
flatRecursion(array)
console.log(newArr);
5.reduce
function flatRecursion2(arr){
return arr.reduce((pre,next)=>{
return pre.concat(Array.isArray(next)?flatRecursion2(next):next)
},[])
}
console.log(flatRecursion2(array));
6.whie+ array.some(Array.isArray)
while(arr.some(Array.isArray)){
arr = [].concat(...arr)
}
复制代码
11. JS异步编程
同步与异步的区别?
同步是阻塞模式 顺序执行 前一个任务执行完才能执行下一任务
异步是非阻塞模式 执行任务不需要一直等前一任务执行完成, 而是继续执行下面的操作
并发:宏观概念 任务交替执行的情况成为并发 同一时间间隔发生
并行:一起执行: 同时完成多个任务的情况的成为并行 同一时间同时发生
最大的区别在于 ”是否同时执行“
es6之前:
-
回调函数
-
事件监听
-
发布/订阅
-
Promise 对象
-
回调函数
根本问题:
- 嵌套函数存在耦合性,牵一发而动全身
- 嵌套函数一朵,很难处理错误
- Generator 函数
Generator 函数 = 状态机+遍历器对象 可以控制函数的执行
- promise
- async/await
setTimeout setInterval requestAnimationFrame
12. 手写系列
call/apply
Function.prototype.myCall= function(context,...args){ // apply时 改成数组传参即可
if(typeof this !=='function'){
throw new Error('Operator should be a function')
}
context = context || window
const fn = Symbol('fn')
context[fn] = this
const result = context[fn](...args)
delete context[fn]
return result
}
复制代码
bind
Function.prototype.myBind = function(context,...args){
if(typeof this !=='function'){
throw new TypeError('not function')
}
let fn = this
let Bound = function(){
fn.apply(this instanceof fn?this:context,[...args,...arguments])
}
Bound.prototype = Object.create(fn.prototype)
return Bound
}
复制代码
防抖/节流
防抖/节流的实现的机制原理: 用闭包给函数加了一个锁,用异步函数更新锁的状态
立即执行与非立即执行的关键点在于 函数和锁状态更新是否同步
// 防抖 高频事件场景下,限制函数在N秒内只执行一次, 如果在N秒内被再次触发,则重置时间,重新执行
function debouce(fn,ms){
let timer = null
return function(...args){
if(timer){clearTimeout(timer)}
timer = setTimeout(()=>{
fn.apply(this,args)
},ms)
}
}
防抖:立即执行版:
// 立即执行
function debounce_1(func,wait){
let timeout
return {
function(){
let args = [].slice.call(arguments);
if(timeout) clearTimeout(timeout);
let callNow = !timeout;
timeout = setTimeout(()=>{
timeout = null;
},wait)
if(callNow) fn.apply(this,args);
}
}
}
// 节流 高频事件场景下,限制函数在一定的频率内执行
function throttle(fn,ms){
let canRun = true
return function(...args){
if(!canRun) return
canRun = false
setTimeout(()=>{
fn.apply(this,args)
canRun = true
},ms)
}
}
//立即执行
function throttle(fn,ms){
let canRun = true
return function(...args){
if(!canRun) return
canRun = false
fn.apply(this,args)
setTimeout(()=>{
canRun = true
},ms)
}
}
复制代码
new
// 1. 创建一个空对象 {}
// 2. 将这个空对象的的原型指向构造函数的原型
// 3. 改变this的指向,指向这个空对象
// 4. 返回新创建的对象
function createNew(fn,...args){
if(typeof fn !== 'function'){
throw new Error('not function')
}
let obj2= Object.create(fn.prototype) // 其他防范: setPrototypeOf() obj2.__proto__
const result = fn.apply(obj2,args)
if(typeof result === 'function' || (typeof result ==='object' && typeof result !==null)){
return result
}
return obj2
}
复制代码
深拷贝
见上方目录5
instanceof
function myInstanceof(instance,klass){
let proto = instance.__proto__
let prototype = klass.prototype
while (true){
if(proto===null || proto ===undefined) return false
if(proto===prototype) return true
proto =proto.__proto__
}
}
复制代码
13. Event Loop 事件循环
进程:描述了CPU在运行指令及加载和保存上下文所需的时间,通常代表了一个程序
线程:进程中更小的单位,描述了执行一段指令所需的时间 JS引擎线程 渲染线程…
执行栈:一个存储函数调用的栈结构,遵循先进后出原则
执行js代码的时候往执行栈中放入函数, 遇到异步代码,会被挂起放入到Task队列中, 一旦执行栈为空,event loop就会从Task队列中取出需要执行的代码并放入执行栈中执行
微任务:microtask 宏任务:macrotask
浏览器中的Event Loop执行顺序:
- 首先JS单线程执行的,在代码执行时,将不同函数的执行上下文压入执行栈来保证代码的有序执行
- 在执行同步代码的时候,如果遇到了异步任务,JS引擎会将这个事件挂起,继续执行执行栈中的其他任务
- 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行
- 执行所有微任务
- 当执行完所有微任务后,如有必要会渲染页面
- 然后开始下一轮Event Loop, 执行宏任务中的异步代码
误区:微任务快于宏任务??
宏任务中包括了script,浏览器会先执行一个宏任务,接下来有异步代码才会先执行微任务
// 请给出下面这段代码执行后,log 的打印顺序
console.log('script start')
async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()
setTimeout(function() {
console.log('setTimeout')
}, 0)
new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {z
console.log('promise2')
})
console.log('script end')
复制代码
Node中的Event Loop
暂无
垃圾回收机制
垃圾回收是一把双刃剑,其好处是可以大幅简化程序的内存管理代码,降低程序员的负担
弊:ECMAScript没有暴露任何垃圾回收器的接口。我们无法强迫其进 行垃圾回收,更无法干预内存管理
为什么需要?
我们知道,在V8引擎逐行执行JavaScript代码的过程中,当遇到函数的情况时,会为其创建一个函数执行上下文(Context)环境并添加到调用堆栈的栈顶,函数的作用域(handleScope)中包含了该函数中声明的所有变量,当该函数执行完毕后,对应的执行上下文从栈顶弹出,函数的作用域会随之销毁,其包含的所有变量也会统一释放并被自动回收。试想如果在这个作用域被销毁的过程中,其中的变量不被回收,即持久占用内存,那么必然会导致内存暴增,从而引发内存泄漏导致程序的性能直线下降甚至崩溃,因此内存在使用完毕之后理当归还给操作系统以保证内存的重复利用
V8的内存限制 64位:1.4G 32位:0.7G
v8采用了分布式垃圾回收机制, 将堆内存分为新生代和老生带两部分
- 新生代: Scavenge算法 牺牲空间换取时间的算法 有From空间和T空间组成 在64位和32位计算机上分别为32m和16m
- 老生代: 其中的对象一般存活时间较长且数量多,使用了标记清除算法+标记压缩算法 、
内存泄露
- 意外的全局变量
- 闭包
- 被遗忘的计时器或回调函数
- 脱离DOM的引用
14.作用域与作用域链
js静态作用域,也可以称为词法作用域
作用域:程序源代码中定义变量的区域
作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。
作用域分类:
- 全局作用域
- 局部作用域
- 函数作用域
- 块级作用域
- with/catch/script/eval/wasm作用域
意思是说作用域是在定义的时候就创建了, 而不是运行的时候
函数的作用域基于函数创建的位置决定的
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope(); //local scope
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()(); // local scope
复制代码
15. 执行上下文与执行栈
JavaScript 代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution context)。
对于每个执行上下文,都有三个重要属性:
- 变量对象(Variable object,VO)
- 作用域链(Scope chain)
- this
作用域链: 当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。
由上图示意,执行上下文栈是一个先进后出的栈结构,关键是 是一个自下往上的叠加结构,以为了,最上层的代码在执行时,可以一层一层的向下操作
由此,可以看出, 变量本质上就是定义在当前执行上下文中的变量对象里,所以只有当前执行上下文采用访问该变量的权限
16. 正则表达式
三个点: 匹配什么 不匹配什么 匹配多少次
17.Ajax
//1:创建Ajax对象
var xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');// 兼容IE6及以下版本
//2:配置 Ajax请求地址
xhr.open('get','index.xml',true);
//3:发送请求
xhr.send(null); // 严谨写法
//4:监听请求,接受响应
xhr.onreadysatechange=function(){
if(xhr.readySate==4&&xhr.status==200 || xhr.status==304 )
console.log(xhr.responsetXML)
}
$.ajax({
type:'post',
url:'',
async:ture,//async 异步 sync 同步
data:data,//针对post请求
dataType:'jsonp',
success:function (msg) {
},
error:function (error) {
}
})
复制代码
18.设计模式
- S – Single Responsibility Principle 单一职责原则
- 一个程序只做好一件事
- 如果功能过于复杂就拆分开,每个部分保持独立
- O – OpenClosed Principle 开放/封闭原则
- 对扩展开放,对修改封闭
- 增加需求时,扩展新代码,而非修改已有代码
- L – Liskov Substitution Principle 里氏替换原则
- 子类能覆盖父类
- 父类能出现的地方子类就能出现
- I – Interface Segregation Principle 接口隔离原则
- 保持接口的单一独立
- 类似单一职责原则,这里更关注接口
- D – Dependency Inversion Principle 依赖倒转原则
- 面向接口编程,依赖于抽象而不依赖于具体
- 使用方只关注接口而不关注具体类的实现
- 创建型
class Singleton {
constructor(){}
}
Singleton.getinstance = (function(){
let instance
return function(){
if(!instance){
instance = new Singleton()
}
return instance
}
})()
const a = Singleton.getinstance()
const b = Singleton.getinstance()
console.log(a===b);
复制代码
19. 字符串方法合集
常见的字符串方法
matchmatchAllreplacesplitsearchindexOfsubstringcharAt(index)
indexOf: indexOf(searchvalue,fromindex) 方法返回某个指定字符串在字符中首次出现的位置
参数:1.必须,检索的字符串值
2.可选,字符串开始检索的位置,合法取值是0-stringObject.length-1,不填,则从开始检索
2. substring(start,stop):提取字符串中介于两个指定下标之间的字符
参数:1.必须,一个非负的整数,要提取的子串的第一个字符在stringObject中的位置
2.可选,一个非负的整数,要比提取的子串的最后一个字符在stringObject中多1,若省略,则匹配到stringObject
3.charAt(index):返回指定位置的字符
参数:1.必须,字符在字符串中的下标
4.split(separator,howmany):把一个字符串分割成字符串数组
参数:1.必须,字符串或正则表达式,从该参数指定的地方分割stringObject
2.可选,指定返回数组的最大长度,如果没有设置参数,整个字符串都会被分割
复制代码
判断字符串是否含有某个值的方法
let str = '123'
console.log(str.indexOf('3')!==-1) 、、
console.log(str.search('3')!==-1);
let reg = new RegExp(/3/)
console.log(str.match(reg))
console.log(reg.test(str))
console.log(reg.exec())
复制代码