1.数组
1.2 创建方式
var arr = new Array(); // 不定长度
var arr = new Array(10) // 定长为10
var arr = [];
1.3 基本操作
- 获取数据、增加数据取的是下标
- in 遍历 返回的是下标
- for of 遍历,返回的是值
- delete 删除数据会存在empty占位符
let arr = [1,2,3];
arr[3] = 4 ;
console.log(arr); // [1,2,3,4]
console.log(arr[0]);
delete arr[3];
console.log(arr); // [1,2,3,empty]
// in 遍历
for(var i in arr) {
// i 是下标索引
console.log(arr[i])
}
for(let i= 0;i<arr.length;i++) {
console.log(arr[i]) // 输出4个,delete 引发的
}
// of 遍历,返回的是值
for(let i of arr) {
console.log('of',i) //1,2,3,undefined
}
1.4 数组的属性
- length 返回数组的长度
- constructor 处理实例接受的外部参数
- prototype 用户扩展额外的方法属性和值属性
console.log('constructor',arr.constructor(1,2,3)) // [1,2,3]
const a = new Array(1,2,3);
console.log('a',a) // [1,2,3]
Array.prototype.getLegnth = function () {
return this.length;
};
let b = [];
console.log('a-getLength',b.getLegnth()) // 0
1.5数组操作方法
// 修改原数组
popArr.push(4); // 改变原数组
popArr.unshift(1); // 改变原数组
popArr.pop(); // 修改原数组
popArr.shift(); // 修改原数组
popArr.splice(1,1,9); // 在1的位置,删除一项,并加入一项9
popArr.reverse(); // 修改原数组
// 不修改原数组
popArr.concat([5,6]) // 不修改原数组
popArr.slice(1,2); // 选择某些连续的项,从下表1开始,到下标2为止,返回长度2-1.
popArr.sort(); // 数字排序
// 转化为以,分割的字符串
console.log('popArr',popArr);
console.log('popArr',popArr.toString()); // '1,3,9'
console.log('popArr',popArr.toLocaleString()); // '1,3,9'
console.log('popArr',popArr.join()); // '1,3,9'
// 迭代方法
const every = popArr.every(i => i>2);
console.log('every',every); // false
const filter = popArr.filter(i => i>10);
console.log('filter',filter); // []
const find = popArr.find(i => i==100);
console.log('find',find) // undefind ,找到返回值
const some = popArr.some((item,index,arr)=> { // 三个参数,item 每一项,index索引,arr原数组
console.log('---',item,index,arr);
return item>1;
});
// 缩小方法 popArr.reduceRight
const a= popArr.reduce((prev,next,index,arr) => { // 2个参数,第一个是一个函数(4个参数,第一个,下一个,索引),返回值是对最后一次处理的next
console.log(prev,next,index,arr)
return next -1;
},100)
console.log('a---',a)
2 函数
2.1 函数的定义
- 静态方法 function a () {}
- 直接量方法 const a = function() {};
2.2 调用方式
- 直接调用 a()
- 在链接中调用 <a herf = 'javascript:a()'>
- 事件调用 = "a()"
- 递归调用
2.3 方法
- apply 将函数作为对象的方法来调用,也就是绑定this的指向,参数为[]
- call 将函数作为对象的方法调用 ,也就是绑定this的指向 ,参数为一个个
- bind 将函数绑定,返回一个函数代执行
- toString 返回函数的字符串表示
2.4对象属性
-
arguments 存放实参数的参数,是一个类数组对象 arguments.length 获取函数实参的长度 arguments.callee 返回正在执行的函数 arguments.caler 返回正在执行函数的函数名字
-
this 指向当前对象
-
prototype 指向参数集合所属函数
2.5原型和原型链
-
什么是构造函数? 函数名字大写 function Persion() {}
-
prototype 是什么,指向什么? 每一个函数都有一个prototype属性,指向 执行 构造函数 创建 的 实例的原型
let persion1 = new Persion(); Persion.prototype 指向 persion1的原型 因此概念为,构造函数(Persion) 实例(persion1) 和实例原型(Persion.prototype)
-
如何描述实例(persion1)和实例原型(Persion.prototype)的关系 每一个js对象,除了了null,都有一个属性__proto__ ,指向实例原型,也就是该对象的原型
persion1.proto === Persion.prototype // true
实例(persion1)对象通过__proto__ 指向原型;构造函数(Persion)通过prototype 指向原型 -
那实例原型是否有属性指向构造函数和实例呢? 指向实例没有,因为实例可能很多个,实例原型只有一个; 指向构造函数有的,每一个实例原型都可以通过 constructor 指向构造函数
Persion.prototype.constructor === Persion ===persion1.constructor // true
实例 实例原型 和构造函数之间的关系就是这些。
-
实例和原型之间的关系 实例通过 proto 找到原型,如果找不到就去找原型的原型,一直往上找,最顶层是 Object.prototype
Persion.prototype.proto === Object.prototype
Object.prototype 的原型是什么呢? Object.prototype.proto 为 null
Object.prototype.proto === null // true
一直往上找的行为会形成一个链条,称为原型链。
表现为: persion1.proto ----> Object.prototype Object.prototype.proto ---> null
-
proto 是什么 浏览器支持的非官方的一个实例属性去指向原型,内部是执行了 Object.getProrotypeOf(persion1)
Object.getPrototypeOf(persion1) === Persion.prototype // true
2.6函数作用域 和执行上下文
- 函数作用域:全局作用域、函数作用域、 eval
- 执行上下文:当代码执行的时候, 会先进行准备工作(变量提升、函数提升),这个准备工作就叫做 执行上下文。
- 如何管理执行上下文: 当执行全局代码,js引擎会创建执行上下文栈,会创建执行上下文栈,这个栈是一个数组。 当遇见函数执行的时候,根据调用,依次会把函数压栈,最上面的先出栈执行,最后是全局(global栈)
- 每个执行上下文都会产生3个重要的对象 变量对象、作用域链、this 全局变量对象就是 window 和 this.window 是一样的
- 作用域链: 查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链
2.7闭包
- 可以访问自由变量的函数(不是参数和函数内部声明的); 即使销毁了函数,也可以通过作用域链访问到变量
2.8 参数按照值传递还是引用传递
var value = 1;
function foo(v) {
v = 2;
console.log(v); //2
}
foo(value);
console.log(value) // 1
var obj = {
value: 1
};
function foo(o) {
o.value = 2;
console.log(o.value); //2
}
foo(obj);
console.log(obj.value) // 2
var obj = {
value: 1
};
function foo(o) {
o = {value:'ddd'};
console.log(o.value); //ddd
}
foo(obj);
console.log(obj.value) // 1
按引用传递是传递对象的引用,而按共享传递是传递对象的引用的副本!
所以修改 o.value,可以通过引用找到原值,但是直接修改 o,并不会修改原值。所以第二个和第三个例子其实都是按共享传递。
2.9 创建对象的方式和优缺点
- 工厂模式
function createPersion(name) {
let o = new Object();
o.name = name;
o.sayName = function() {}
return o;
}
缺点:对象无法识别,所有实例都指向同一个原型
- 构造函数模式
function Persion(name) {
this.name = name;
this.getName = function() {}
}
优点:实例可以识别为一个特定的类型
缺点:每次创建实例时,每个方法都要被创建一次
优化:
function Person(name) {
this.name = name;
this.getName = getName;
}
function getName() {
console.log(this.name);
}
var person1 = new Person('kevin');
失去了封装性
- 原型模式
function Person(name) {
}
Person.prototype.name = 'keivn';
Person.prototype.getName = function () {
console.log(this.name);
};
var person1 = new Person();
优点:方法不会重新创建
缺点:1. 所有的属性和方法都共享 2. 不能初始化参数
优化:
function Person(name) {
}
Person.prototype = {
constructor: Person,
name: 'kevin',
getName: function () {
console.log(this.name);
}
};
var person1 = new Person();
原型的缺点还是没有避免
- 组合模式 构造函数和原型的组合模式,也是使用比较广泛的一种
```javascript
function Persion(name) {
this.name = name;
}
Persion.prototype = {
constructor:Persion,
getName:function() {}
}
优点:该共享的共享,该私有的私有,使用最广泛的方式
缺点:有的人就是希望全部都写在一起,即更好的封装性
```
- 动态原型模式
方式1:
function Persion(name) {
this.name = name;
if(typeof this.getName !== 'function') {
Persion.prototype.getName = function() {}
}
}
方式2
function Person(name) {
this.name = name;
if (typeof this.getName != "function") {
Person.prototype = {
constructor: Person,
getName: function () {
console.log(this.name);
}
}
return new Person(name);
}
}
-
寄生构造函数模式
function Persion(name){ var o = new Object(); o.name = name; o.sayName =function() {} return o; } 什么时候使用,假设想要一个数组,有个方法,返回以| 连接数组的每一项的字符串 function SpecialArray() { var values = new Array(); for (var i = 0, len = arguments.length; i < len; i++) { values.push(arguments[i]); } values.getSpiString =function() { return this.join('|'); } } -
稳妥模式 没有公共属性,也不引用this
function person(name){
var o = new Object();
o.sayName = function(){
console.log(name);
};
return o;
}
2.10 继承方式
-
原型链继承 原理: 子的原型指向父级的实例原型 const A.prototype = new Parent(); 缺点: 1. 引用类型的属性被所有实例共享 2.在创建 Child 的实例时,不能向Parent传参
function Parent() {}; function Child() {}; Child.prototype = new Parent(); -
借助构造函数 原理: 子构造函数里面调用父构造函数,父构造函数调用绑定子的this 优点: 1.避免了引用类型的属性被所有实例共享 2.可以在 Child 中向 Parent 传参 缺点: 方法都在构造函数中定义,每次创建实例都会创建一遍方法。
function Parent(name) {} function Child(name) { Parent.call(this,name) } -
组合继承 原理:原型式继承和组合继承结合的方式, 父级方法放在原型上,可以解决构造函数的缺点 优点:解决了原型链继承和原型式继承 组合继承最大的缺点是会调用两次父构造函数。
一次是设置子类型实例的原型的时候:
Child.prototype = new Parent(); 一次在创建子类型实例的时候:
var child1 = new Child('kevin', '18');
function Parent(name){} Parent.prototype.sayName = function() {} function Child(name) { Parent.call(this,name) } Child.prototype = new Parent(); Child.prototype.constructor = Child; -
原型式继承 原理:传入的对象作为创建的对象的原型,就是 ES5 Object.create 的模拟实现 缺点:和原型链继承是一样的
function createObj(o) { function F () {} F.prototype = o; return new F() } -
寄生式继承 原理: 创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
function createO(o) { let obj = Object.create(o); obj.sayName = function() {} return obj; } -
组合寄生式继承 原理: 组合模式下,如果让Child.prototype 间接的指向 Parent.prototype
function Parent(name){} function Child(name) { Parent.call(this,name) } let F = function() {} F.prototype = Parent.prototype; Child.prototype = new F(); 最后把这个方法封装一下 function prototype(child, parent) { var prototype = Object.create(parent.prototype);// 创建过度的对象 prototype.constructor = child; // 对象的构造函数指向 child child.prototype = prototype; // 指向中间过渡原型 } // 当我们使用的时候: prototype(Child, Parent);
web浏览器的事件注册
事件挂在
Element,Document和Window
-
方式1: 通过把函数赋给某个特殊属性 缺点:对于某个事件只能注册一个事件处理器。也就是说, 一不小心就会将上一个事件处理器改写掉
window.onload =funciton() {} document.body.onclick = function() {} -
方式2: 通过addEventListener 可以注册多个事件
window.addEventListener('click',function(e,i) { console.log(e,i) }); document.body.addEventListener('click',function(e) { console.log(e) })
3 编程方式
3.1命令式编程 VS 面对对象编程 VS 函数式编程
- 命令式编程: 用控制语句(if else while return break)等,一步步告诉计算机先做什么,后做什么;
- 面对对象编程:告诉计算机应该做什么,但是不需要告诉计算机如何做。
- 函数式编程:将计算机计算看成是函数的计算。 这几种编程思想,并不是割裂的独立的,只是一定程度的上的代码思想的转化,命令式适合做流程控制(switch); 面对对象适合构建实例,操作并维护一个仓库;函数式编程基于函数为基础去编程
// 命令式
trim(reverse(toUpperCase(map(arr))));
// 面向对象
arr.map().toUpperCase().reverse().trim();
// 函数式
const result = componse(trim,reverse,toUpperCase,map)
3.2什么是函数式编程
原子组合:把原本的逻辑组合,并不影响最后的结果(加法结合律 | 因式分解 | 完全平方公式)
3.3函数式理论思想
- 函数式一等公民
- 逻辑功能落脚点就是函数
- 实现函数和拼接函数
- 声明一个需求,尽量语义化
3.4函数式的使用场景
函数组合的形式
- 函数组合流水线有顺序的(后面的输入依赖前面的输出,违背了函数式原则,但是还是能优化点代码)
- 函数组合流水线无顺序的(函数前后调用返回结果不变,arr.map.filter)
/**
*
* // 第一次需求: 将这个人的名字加个加个企业名字
* // 第二次需求: 将这个人名字加上后缀 属于企业有限责任公司
* // 第三次需求: 将这个人的公司名称隐藏 **
*
* // 函数式组合的概念:
* 1. 首先创建一个组合函数,本次的输出作为下次的输入(reduce)
* 2. 返回一个缓存函数
* 3. 执行缓存函数即可
*/
const componse = (...fns) => {
return (value) => {
return fns.reduce((v,fn) => fn(v),value)
}
}
const addName = (str) => {
return `${str}-属于企业有限责任公司`
}
const hideName = (str) => {
return str.replace(/公司/ig,'**')
}
const elipName = (str) => {
return str >=10?`${str}...`:str;
}
const comData = componse(addName,hideName,elipName);
console.log('comData', comData('ffsdfsafjf'))
// 上面一个例子都有个问题,就是不够封闭性,尝试将企业的名字动态传入
const newAddName =(companyName) => {
return (value) => value + companyName
};
const comData = componse(newAddName('测试企业名字'),hideName,elipName);
偏函数
概念:缓存一部分参数,然后让另外一些参数在使用时传入
const newAddName =(companyName) => {
return (value) => value + companyName
};
#### 柯里化
概念:将参数放到一起,最后一起处理.
```js
// 实现一个去重数据的函数
// f([1,2])([3,4])([1,2]) => [1,2,3,4]
const unquieArrFun =(value=[]) => {
let arr =[...value];
const innerFun =(innerValue=[]) => {
arr = [...arr,...innerValue];
return innerFun
}
innerFun.toString =() => {
return [...new Set(arr)];
}
return innerFun;
}
const data = unquieArrFun([1,2])([3,4])([1,2])
console.log('unquieArrFun',data.toString())
// 实现一个累加
// f(1)(2)(3)(4)
const addFun =(value:number) => {
const arr =[value];
const innerFun =(val:number ) => {
arr.push(val);
return innerFun;
}
innerFun.toString =() => {
return arr.reduce((prev,next) => prev+next,0)
}
return innerFun;
}
const sum = addFun(1)(2)(3);
console.log('sum',sum.toString())
```
惰性函数
概念:调用一次之后,后面再调用一直都是同一个函数
// 需求:取一个基础时间点,任何时候都要以此时间点计算
const getTime1 = () => {
return new Date().getTime();
}
const getTime = () => {
let time = new Date().getTime();
const getTime = () => {
return time;
}
return getTime();
}
setTimeout(() => {
console.log('----', getTime1())
console.log('----', getTime())
},200)
setTimeout(() => {
console.log('----2', getTime1())
console.log('----2', getTime())
},300)
// 需求:缓存请求
let cacheApi = async () => {
const data = await new Promise((resolve,reject) => {
setTimeout(() => {
resolve('eeee');
},2000)
})
console.log('数据只会执行一次')
cacheApi =() => {
return data;
}
return cacheApi();
}
console.log('cacheApi', cacheApi())
setTimeout(() => {
console.log('cacheApi', cacheApi())
},3000)
// 惰性函数
const program = name => {
if(name === 'progressive') {
return program = () => {
console.log('progressive')
}
} else if(name === 'obj') {
return program =() => {
console.log('obj')
}
} else {
return program =() => {
console.log('func')
}
}
}
program('progressive')();
console.log('lazy');
program();
// progressive lazy progerssive
3. 无状态和副作用(rxjs、lodash)
- 无状态 - 幂等,每次输入,得到都是相同的输出
- 无副作用 函数内部不应该对整个系统任何参数变量的改动
if(a === '1' || a ==='2') {
// doSomething
}
// 优化1:
if(['1','2'].includes(a)) {
// doSomething
}
// 优化2:
const functionCode =(a,arr=[]) => { // 消除全局影响
return arr.includes(a)
}
// 优化3:
const functionCode =(a:string| number,arr?:any []):boolean=>(a,arr=intialArr) => { // 全局枚举集合
return arr.includes(a)
}
3.1 实际开发
3.2 纯函数改造
const _class = {
name:"obj"
}
// 函数内部引入了外部变量了, -- 违反了无状态规则
const score = str => _calss.name + ':' + str;
// 违反了修改传入参数
const changeClass = (obj,name) => {
obj.name = name;
}
changeClass(_class,'fucntion');
score('good!');
//修改----------------
const _class = {
name:"ob"
}
const score =(obj,str) => obj.name + ':' +str; // 不依赖外部变量
const changeClass = (obj,name) => ({...obj,name});// 未修改外部变量
changeClass(_class,'fucntion');
score('good!');
3.3流水线组装
实现构造函数实现可拆分传递参数的累加函数
// 1. 构造函数科里化结构
// 2. 传入参数arguments
// 3. 递归参数无限拓展
// 4. 主功能实现 => 累加
// 5. 输出
const add = function() {
let args = Array.prototype.slice.call(arguments);
// 内层处理
const inner = function() {
args.push(...arguments);
return inner;
}
// 递归保证的是都一样,最后输出值,用toString方法复写
inner.toString = function() {
return args.reduce(prev,cur) => {
return prev + cur;
}
}
return inner
}
4正则
4.1 正则的概念
正则是匹配模式,要么是匹配字符,要不匹配位置。
4.2 横向模糊匹配
- 概念: 一个正则可匹配的字符串的长度不是固定的,可以是多种情况的
- 实现方式,使用量词{m,n} 最少m个,最多n个
const patter = /ab{2,5}c/ig;
'abbc abbbc'.match(patter); // [abbc,abbbc]
4.3 纵向模糊匹配
- 概念:一个正则匹配的字符串,具体到某一位字符时,它可以不是某个确定的字符,可以有多种 可能。
- 实现方式:[123] 可能是1,2,3中的任意一个
- 范围表示法[1-6a-zA-Z],表示其中的任意一个,如果仅仅想表示az-的3个字符中任意一个,可以[az-] [za-] [-az]
const patter = /a[123]b/g;
'a0b a1b a2b'.match(patter) // [a1b,a2b]
-
排除字符组 [^abc] 不是abc中的任意一个
- 匹配任意字符 [\s\S] [\d\D] [\s\S] [^]
4.4 量词
{m,n} 最少m次,最多n次 {m,} 最少m次 {m} 出现m次 ? {0,1} 记忆表示有吗
- {1,} 至少有一次 + ,多余一个才加
- {0,} 表示0到多个,* 星星,天上可能看不到,也可能看到很多
- 贪婪匹配 vs 惰性匹配
let regex = /\d{2,5}/g; // 能者多劳 2-5个 let regex1 = /\d{2,5}?/g; // ? 你知足了吗,当为2就不再往下匹配了 '12432,542'.match(regex1) // [12,43,52]
4.5 多选分支
- (p1|p2|p3) 惰性匹配
var regex = /good|goodbye/g;
var string = "goodbye";
console.log( string.match(regex) );
// => ["good"]
4.6 ------------------案例分析--------------------
- 匹配颜色值 #开头 固定为6位或者3位的 数字和大小写字符组成
/#[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/
- 匹配时间 第一个是0-2; 第二位0-9 ;;第三位0-5,第四位0-9
/^([01][0-9]|[02][0-3]):([0-5][0-9])$/
23:35
如果要求匹配 7:9, 十分的0可以省略 ,枚举所有的可能性 /^(0?[0-9]|1[0-9]|2[0-3]):(0?[0-9]|[1-5][0-9])$/
正则匹配位置攻略
如何理解: 出现这几个,或者想操作字符位置的话,用这几个
^ 开头
之间的位置。
\B 非单词边界
(?=p) 子模式 匹配到p前面的位置
(?!p) 非子模式 匹配到非p前面的位置
'[sa]saf.32f'.replace(/\b/g,'#'); // '[#sa#]#saf#.#32f#'
'[eea]sdjdd'.replace(/(?=dj)/g,'#')// '[eea]s#djdd'
-
实现数字按照正则匹配 '1241214' 以,千分位分割 每三位是一个模式,从后面开始
'1241214'.replace(/(?=(\d{3})$)/g,',') // '1241,214'每一个三位都要有
'1241214'.replace(/(?=(\d{3})+$)/g,',') // '1,241,214'3的倍数,就会有问题,前面多一个,要求这个位置不能是开头 (?!^)
'1241214'.replace(/(?!^)(?=(\d{3})+$)/g,',') // '1,241,214' -
密码验证 必须包含数字和字母 必须包含s数字 (?=.[0-9]) 必须包含字母 (?=.[a-z])
?=.*[0-9] // .*任意字符后面必须是数字
/(?=.*[0-9])^[0-9A-Za-z]{6,12}$/
-
反向引用
2016-06-12 2016/06/12 2016.06.12
/\d{4}(/|.|-)\d(2)(/|.|-)\d{2}/g;
也会匹配到 2016.06-12 这样的内容
/\d{4}(/|.|-)\d(2)\1\d{2}/g; // \1表示上一个匹配到的分组
正则表达式括号的使用
- 括号提供了分组,可以在js中引用或者在括号中引用
- (ab)+ 表示以ab出现最少一次;
- (ab|abc) ab 或者 abc
- 匹配到的分组可以用表示 2 (api分组)
'2027-12-34'.replace(/(\d{4})-(\d{2})-(\d{2})/,'$3.$2.$1'); // '34.12.2027'
-
反向引用 含义:让你少写一些一模一样的正则, 在正则里面使用 \1 \2 \n 表示匹配到的分组的简写形式 \10 表示第十个分组
/(\d{1,})(-)\1\2/g.test('123-123-') // true (\d{1,}) 用\1代替 (-) 用\2代替- 非捕获括号 只想要匹配到最原始的功能,可以使用非捕获括号 (?:P)
let reg = /(?:ab)+/g; 'ababc abbbb ababab'.match(reg); // ['abab','ab','ababab'] 只要ab连续子串
正则表达式的拆分
正则执行的优先级 \ ---> () [] ---> {m,n} ? * + ----> ^ $ \1 \b \B ---> |
元字符转译
需要,但是不完全需要,为避免错误,尽量都转译
/[abc]/g === /[abc]/g 因为后面一个构不成字符组[],因此不需要
是否使用正则
- 正则不能完成所有事情,能用字符串方法,不用正则
- 构建小而简单的正则,去判断处理,不需要构建一个复杂看不懂的正则
- 准确性,一般是知道预期的目标,而不匹配非预期的目标
正则表达式回溯原理
-
概念 从一种状态出发,搜索这种状态的所有可能,当一条路走不通,再后退一步或者若干步。这种不断前进,不断后退的方法,称为回溯法。
-
发生在哪些地方 贪婪量词 {m,n} 惰性量词 {m,n}? 分支结构 |
5 字符串
5.1 字符串函数
5.2 根据位置查找字符函数
- charAt(n) 返回字符串中第n字符,从字符串的下标0开始
- charCodeAt(n) 返回字符串中n哥字符的code 是多少,返回是数字
'ddddasf'.charAt(5) // s
'ddddasf'.charCodeAt(5) // 115
5.3根据字符查找位置函数
-indexOf() 从前往后找,找到第一个符合的 没找到返回-1 -lastIndexOf() //从后往前找,找到第一个符合的没找到返回-1
'ddddasf'.indexOf(s) // 5
'ddddasf'.lastIndexOf(s) // 5
5.4匹配函数
-
match 找到一个或者多个正则表达式或者字符串的匹配
'141414'.match(/14/) // ['14', index: 0, input: '141414', groups: undefined] 'fada'.match(/a/ig) // ['a','a'] 全局匹配额时候 'afda'.match('a') // ['a', index: 0, input: 'afda', groups: undefined] -
search 找到第一个要查找的字符串,没找到返回 -1
'afda'.search(/a/) // 0 'afda'.search('a') // 0 -
replace 替换参数,接受一个字符或者正则表达式
'14124'.replace('1','s')// 's4124' 替换一个 '14124'.replace(/1/,'s')// 's4124' 替换一个 '14124'.replace(/1/ig,'s') // 全替换 必须加g '14124'.replace(/1/g,function(){ return 'j' }) // 'j4j24' 接受函数 '1,4124'.replace(/(1),(4)/g,'$2,$1') // $n 表示匹配到的分组占位符 -
split 以一定的规则分割成数组
'daf'.split('') // [d,a,f]
- concat 链接字符串,功能和 + 一样
'a'.concat('2') // a2
5.5截取方法
- slice subString 通过下标获取字符串方法
- substr 根据长度截取
'a23456'.slice(1,2) // 2
'a23456'.subStrig(1,2) // 2
'a23456'.substr(1,2) // 23
5.6去掉空格
- trim() 首尾去掉
- trimLeft() 去掉左边
- trimRight()
5.7 编码方法
-
escape unescape 对字符串进行uncoide 编码
-
encodeURI uncodeURI 对url编码,不会对 / ?,等编码
-
encodeURIComponent uncodeURLComponent 对所有的URI编码,会导致路由不能用
变量类型
基础类型
类型: null boolean string number undefined Symbol Bigint 检查: typeof 存储: 栈中,值类型 typeof返回的值: string number undefined boolean function object symbol bigint(全是小写)
对象类型
类型: null boolean string number undefined Symbol Bigint 检查: instanceof 存储: 堆中,引用类型 instanceof返回的值: Object Array Function String Date RegExp Number Set WeakSet Map WeakMap(全是大些)
变量检查类型
基础类型检查
const a_1 = null;
const a_2 = 'tedt';
const a_3 = 12;
const a_4 = true;
const a_5 = undefined;
const a_6 = Symbol('ll');
const a_7 = BigInt('12');
const a_8 = () => {};
const a_9 = {};
const a_10 = [];
console.log(typeof a_1 === 'object') //true
console.log(typeof a_2 === 'string') //true
console.log(typeof a_3 === 'number') //true
console.log(typeof a_4 === 'boolean') //true
console.log(typeof a_5 === 'undefined') //true
console.log(typeof a_6 === 'symbol') //true
console.log(typeof a_7 === 'bigint') //true
console.log(typeof a_8 === 'function') //true
console.log(typeof a_9 === 'object') //true
console.log(typeof a_10 === 'object') //true
复杂类型检查
const b_1 = {};
const b_2 = [];
const b_3 = new Date();
const b_4 = /qwe/g;
const b_5 = () => {};
const b_6 = new String('123');
const b_7 = new Number('123');
const b_8 = new Set([1]);
const b_9 = new WeakSet();
const b_10 = new Map();
const b_11 = new WeakMap();
console.log('-----------')
console.log(b_1 instanceof Object); // true
console.log(b_2 instanceof Object); // true
console.log(b_2 instanceof Array); // true
console.log(b_3 instanceof Date); // true
console.log(b_4 instanceof RegExp); // true
console.log(b_5 instanceof Function); // true
console.log(b_6 instanceof String); // true
console.log(b_7 instanceof Number); // true
console.log(b_8 instanceof Set); // true
console.log(b_9 instanceof WeakSet); // true
console.log(b_10 instanceof Map); // true
console.log(b_11 instanceof WeakMap); // true
当判断一个引用类型,是不是对象时,可能是很多种,因此需要一个准确判断方法。Object.prototype.toString() 判断格式: '[object Type]' object 是小写的,Type是大写的
console.log(Object.prototype.toString.call(a_1) === '[object Null]'); // true
console.log(Object.prototype.toString.call(a_2) === '[object String]'); // true
console.log(Object.prototype.toString.call(a_3) === '[object Number]'); // true
console.log(Object.prototype.toString.call(a_4) === '[object Boolean]'); // true
console.log(Object.prototype.toString.call(a_5) === '[object Undefined]'); //true
console.log(Object.prototype.toString.call(a_6) === '[object Symbol]'); // true
console.log(Object.prototype.toString.call(a_7) === '[object BigInt]'); //true
console.log(Object.prototype.toString.call(a_8) === '[object Function]'); // true
console.log(Object.prototype.toString.call(a_9) === '[object Object]'); //true
console.log(Object.prototype.toString.call(a_10) === '[object Array]'); //true
console.log(Object.prototype.toString.call(b_1) === '[object Object]'); //true
console.log(Object.prototype.toString.call(b_2) === '[object Array]'); //true
console.log(Object.prototype.toString.call(b_3) === '[object Date]'); //true
console.log(Object.prototype.toString.call(b_4) === '[object RegExp]'); //true
console.log(Object.prototype.toString.call(b_5) === '[object Function]'); //true
console.log(Object.prototype.toString.call(b_6) === '[object String]'); //true
console.log(Object.prototype.toString.call(b_7) === '[object Number]'); //true
console.log(Object.prototype.toString.call(b_8) === '[object Set]'); //true
console.log(Object.prototype.toString.call(b_9) === '[object WeakSet]'); //true
console.log(Object.prototype.toString.call(b_10) === '[object Map]'); //true
console.log(Object.prototype.toString.call(b_11) === '[object WeakMap]'); //true
变量的作用域
全局变量 位置:全局var、函数体内无var 调用:任意位置 生命周期:除非显式的删除、否则一直存在,挂在在window上 局部变量 位置:函数参数、函数体内的var声明 调用:当前的函数体内 生命周期:自函数执行起或者函数 优先级: 局部变量同名高于全局变量同名 参数同名高于全局变量参数同名 局部变量同名高于参数同名
var widowA = '全局'
function testName (widowA) {
widowA = '局部';
console.log('widowA',widowA) // 局部
return widowA;
};
testName('参数');
console.log('widowA',widowA) // 全局
var b = 'ddd';
function testB() {
b = 'ccc';
};
testB();
console.log('b',b); // ccc
作用域链: 函数内层能访问外层和全局的变量 外层函数不能访问内层局部变量
声明提升: 函数声明要高于变量声明
console.log(m); // f() {}
var m ='';
function m(){};
6 变量隐式类型转换
6.1 概念: 通过显示的 Number('123') 称为显示类型转换;
一些情况下,来种值会发生相互转化,称为隐式类型转换;
=== 不会发生隐式类型转换。
6.2 规则:
规则_1:js中只有三种类型转换 (to string ; to boolean ; to number )
规则_2:原始类型和对象类型转化是不同的,但是只会转成上面三种类型
6.3 转化为string:
显式: String('12') new String('12')不是值,是一个对象
基础类型 隐式转换 格式: [p] + ''
console.log('to string',12 + ''); // '12'
console.log('to string','12' + ''); // '12'
console.log('to string',null + ''); // 'null'
console.log('to string',undefined + ''); // 'undefined'
console.log('to string',true + ''); // 'true'
console.log('to string',BigInt(1) + '1'); // '11'
console.log('to string',String(Symbol('d'))); // 'Symbol(d)' Symbol 必须显式转换
6.4 转换为boolean
不在以下情况的都转化为true
Boolean('') // false
Boolean(0) // false
Boolean(-0) // false
Boolean(NaN) // false
Boolean(null) // false
Boolean(undefined) // false
Boolean(false) // false
发生隐式转换的情况
if('2' || '') // || 和 && 会发生内部转换,可以判断,但是返回的是值,
!!2
6.5 number 转化
比较复杂, 比较操作(>, <, <=, >=) 按位操作(| & ^ ~) 算数操作(- + * / %), 注意,当 + 操作存在任意的操作数是 string 类型时,不会触发 number 类型的隐式转换 一 元 + 操作 非严格相等操作(== 或者 != ),注意,== 操作两个操作数都是 string 类型时,不会发生 number 类型的隐式转换
== 的转化规则,当位null 和 undefined 时,不会发生转化。null和undefined 只等于自己本身
+ '124' // 124
'123' - 0 // 123
Number(true) // 1
Number(undefined) // NaN
Object 类型转换 规则: -Symbol.toPrimitive 存在,执行这个,不走valueOf、toString ,是原始类型就返回,还不是原始类型就保存。 -number 先调用 valueOf() 后调用toString() ; string 先调用 toString() 后调用 valueOf()
let testObjToNumber ={
// [Symbol.toPrimitive](hite) {
// if(hite ==='number') {
// return 3;
// }
// if(hite === 'string') {
// return 4
// }
// },
a:1,
valueOf() {
return this.a +1;
},
toString() {
return this.a+'1'
}
};
console.log('testObjToNumber',+testObjToNumber) // 2, 放开3
console.log('testObjToNumber',`${testObjToNumber}`) // 11 ,放开返回是4