every day 87-21/01/11
遇到问题记录
公众号 h5 开发问题
ios 授权后,屏幕底部的有回退箭头,点击会返回授权的那个空白页 原因:是 history 里有记录之前的的历史记录,所以 ios 端会有箭头返回 解决:用 history.replaceState() 去替换历史记录,浏览器地址栏会变成你传的地址,而页面并不会重新载入或跳转。让他回退还是当前页面
CSS 篇
css 加载不会阻塞 DOM 树解析,但是会阻塞 DOM 树渲染
1、css 加载不会阻塞 DOM 树的解析 2、css 加载会阻塞 DOM 树的渲染 3、css 加载会阻塞后面 js 语句的执行
line-height:2 与 line-height:200% 区别
line-height:2 单行文本是自身高度的两倍行高 ling-height:200 单行文本是系统默认字体的 200%
继承性 父元素设置 line-height:2,父元素的子元素会继承它,子元素的行高是自身高度的两倍。所以父元素跟子元素行高不相同 父元素设置 line-height:200%,同样会继承,但是系统默认字体是固定的,所以父元素、子元素的行高是一样的
// 回流也是重排,就是改变了dom的结构,例如div大小或位置、边框等。浏览器消耗大,回流(重排)一定会重绘
// 重绘,就是改变了样式 颜色和背景色等 浏览器消耗比较小 不一定触发重排(回流)
图片
background-size: cover;
background-position:center;
// 图片不拉伸
background-repeat: repeat no-repeat; // 背景图不平铺拉伸
object-fit: cover;
@keyframes 动画
创建动画 animation: 12s 剧本名 linear infinite normal;
animation-name 规定需要绑定到选择器的 keyframe 名称。。
animation-duration 规定完成动画所花费的时间,以秒或毫秒计。
animation-timing-function 规定动画的速度曲线。
animation-delay 规定在动画开始之前的延迟。
animation-iteration-count 规定动画应该播放的次数。
animation-direction 规定是否应该轮流反向播放动画。
@keyframes example(动画名字){
10%{
}
100%{
}
}
api(一些实用,常用或不常用的方法 && api)
let link = document.createElement('a')
link.style.display = 'none'
link.href = res.data.download_url
document.body.appendChild(link)
link.click()
document.bodyNaNpxoveChild(link)
//下载视频
visibilitychange // 浏览器判断页面是否切换
document.addEventListener("visibilitychange", function(){ //判断页面是否隐藏
document.title = document.hidden ? "用户离开了" : "用户回来了";
});
算两个时间之间的天数 const dayDif = (date1, date2) => Math.ceil(Math.abs(date1.getTime() - date2.getTime()) / 86400000) dayDif(new Date("2020-10-21"), new Date("2021-10-22")) // Result: 366
使用 navigator.clipboard.writeText 轻松将任何文本复制到剪贴板
const copyToClipboard = (text) => navigator.clipboard.writeText(text);
copyToClipboard("Hello World");
跨域
基于 ajax 的同源策略,ajax 不允许访问不同域名下的资源(只有前端存在,后端不存在)
产生跨域的情况有:不同的协议,不同域名,不同端口及域名和 ip 地址的访问都会产生跨域
解决方案
1、使用 jsonp 2、vue 用 devServer 本地代理解决开发环境跨域 3、CORS
JS 篇
Object.prototype.toString.call() // 万能检测类型
js 任务(单线程)
www.cnblogs.com/leiting/p/1… or segmentfault.com/a/119000001… 参考文章
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。所以会有任务队列的概念。正因为是单线程,所以所有任务都是主线程执行的,异步请求这些也不会开辟新的线程,而是放到任务队列,当这些异步操作被触发时才进入主线程执行。
同步(同步任务是主线程上进行的,只有上一个任务完成才会下一个任务)
异步(异步任务不进入主线程,只进入任务队列。任务队列通知主线程,某个异步任务才能执行。该任务才会进入主线程执行)
宏任务(macrotask):setTimeout、setInterval、setImmediate、I/O、UI rendering
宏任务按顺序执行,且浏览器在每一个宏任务之间渲染 浏览器为了能够使得 js 的 task 与 DOM 任务有序的进行,会在一个 task 执行结束后,在下一个 task 执行开始前,对页面进行重新渲染(task--> 渲染 -->task....) 只要主线程空了(完成一个 task 后都会来读取任务队列里的任务),就会去读取“任务队列”,这就是 jiavascript 的运行机制,这个过程会不断重复
微任务(microtask):promise.then、process.nextTick、MutationObserver、queneMicrotask(开启一个微任务)
微任务一般都是在宏任务执行后立即执行
所有微任务也按顺序执行,且在以下场景会立即执行所有微任务
1、每个回调之后且 js 执行栈中为空。 2、每个宏任务结束后。
继承
原型链继承(继承的改变,原型也会变)让新实例的原型等于父类的实例。
function Person(){
this.name = '我是父辈'
}
Person.prototype.getName = function (){
console.log(this.name,'我打印了')
}
function son (){
}
son.prototype = new Person()
let son1 = new son()
son1.getName() //我是父辈,我打印了
优点:实例可继承的属性有:实例的构造函数的属性,父类构造函 数属性,父类原型的属性。(新实例不会继承父类实例的 属性!)
缺点:
1、新实例无法向父类构造函数传参。 2、继承单一。 3、所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性 也会被修改!)
构造函数继承(继承者改变不会影响原型值)
function person (){
this.name = '啦啦啦',
this.colors = ['pink','blue','green','red']
}
person.prototype.getname = function(){
console.log(this.name)
}
function son(age){
person.call(this)
// person.apply(this)
this.age = age
this.name = '我是 son'
}
let son1 = new son(18)
let son2 = new son(22)
son1.colors.push('yellow')
console.log(person.length,person.name)
console.log(son1.colors)
console.log(son2.colors)
重点:用.call()和.apply()将父类构造函数引入子类函数(在子类 函数中做了父类函数的自执行(复制)
优点:
- 1、只继承了父类构造函数的属性,没有继承父类原型的属性。
- 2、解决了原型链继承缺点 1、2、3。
- 3、可以继承多个构造函数属性(call 多个)。
- 4、在子实例中可向父实例传参。
缺点:
- 1、只能继承父类构造函数的属性。
- 2、无法实现构造函数的复用。(每次用每次都要重新调用)
- 3、每个新实例都有父类构造函数的副本,臃肿。
组合继承
function person(name) {
this.name = name;
this.colors = ['red','pink','blue']
}
person.prototype.getname = function (){
console.log(this.name+this.sex)
}
function child(name,age,sex){
person.call(this,name) // 第二次调用 person()
// person.apply(this,[name])
// call 参数是逗号隔开传递,apply 参数需数组传递
this.age = age
this.sex = sex
}
child.prototype = new person() // 第一次调用 person()
let child1 = new child('第一个',22,'妹')
let child2 = new child('第 tow 个',26,'仔')
child1.colors.push('haha')
console.log(child1,child2)
child2.getname()
重点:结合了两种模式的优点,传参和复用
优点:
- 1、可以继承父类原型上的属性,可以传参,可复用。
- 2、每个新实例引入的构造函数属性是私有的。
缺点:调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。
call、apply、bind
call() 第一个参数是指向的值,第二个参数可以是数组作为传值,也可以逗号隔开传 apply() 第一个参数是指向的值,第二个参数是数组 or 伪数组 bind() 第一个参数是指向的值,后面参数(返回一个新的对象!!!!)
function fun () {
console.log(this.name,arguments)
}
let obj = { name :'我是定义的对象'}
fun.call(obj,'哈哈哈,传的值','第二个值')
fun.apply(obj,['哈哈哈,传的值'])
let newobj =fun.bind(obj,[1,2,3])
手写 call()、apply()、bind()
Function.prototype.bind = function(){
var self = this // 保存函数
context = [].shift.call(argumentes) // 需要绑定的上下文
args = [].self.call(arguments) // 剩余参数
return function(){
return self.apply(context,[].concat.call(args,[].slice.call(arguments)))
// 执行新的函数时候,会把之前传入的context当作新函数体内的this
// 并且组个两次分别传入的参数,作为新函数的参数
}
}
switch caser 和 if else if
/**
* Switch case 比 if()elseif()快,switch case 用的随机访问,就是比if快,if是遍历去判断
* 常量switch 和 随机数 if快
*
*/
箭头函数 this 的 指向
箭头函数没有自己的 this, 它的 this 是继承而来; 默认指向在定义它时所处的对象(宿主对象),而不是执行时的对象, 定义它的时候,可能环境是 window; 箭头函数可以方便地让我们在 setTimeout ,setInterval 中方便的使用 this 箭头函数的 this 是在定义函数时绑定的,不是在执行过程中绑定的。简单的说,函数在定义时,this 就继承了定义函数的对象 1、箭头函数没有 prototype(原型),所以箭头函数本身没有 this 箭头函数的 this 在定义的时候继承自外层第一个普通函数的 this。 2、如果箭头函数外层没有普通函数,严格模式和非严格模式下它的 this 都会指向 window(全局对象) 3、箭头函数本身的 this 指向不能改变,但可以修改它要继承的对象的 this。 4、箭头函数的 this 指向全局,使用 arguments 会报未声明的错误。 5、箭头函数的 this 指向普通函数时,它的 argumens 继承于该普通函数 6、使用 new 调用箭头函数会报错,因为箭头函数没有 constructor 箭头函数不支持 new.target 7、箭头函数不支持重命名函数参数,普通函数的函数参数支持重命名 8、箭头函数相对于普通函数语法更简洁优雅
闭包
一个函数可以访问内部函数变量
function makeAdder(x) {
let y = 12
return function(y) {
return x + y;
};
}
var add5 = makeAdder(5);
console.log(add5(2)); // 19
function foo(){
const str = '函数内部字段'
function bar () {
return str
}
return bar
}
防抖节流函数
防抖:n 秒后执行该事件,若 n 秒内重复触发,则重新计时 节流:n 秒内只运行一次,若 n 秒内重复触发,只有一次生效
作用域
作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。换句话说,作用域决定了代码区块中变量和其他资源的可见性 (全局作用域和函数作用域、局部作用,ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数作用域。ES6 的到来,为我们提供了‘块级作用域’,可通过新增命令 let 和 const 来体现)
解构赋值
var a, b;
var c = ['啊', '吧' , '哈']
var [a, ...b] = c
console.log(a, b) // 啊, ['吧', '哈']
var C = {obj:'123', obj1:'321'}
var {obj, obj1} = C
console.log(obj,obj1) // 123 321
Array for of & Obj for in 的遍历
for of 循环,item 是数组键值(返回数组的下标对应的属性值)
注意!!!:需要包装一下,不然:not iterable
for(let item of arr){
}
for in 循环,item 是对象键名(返回对象中所有可枚举的属性(包括原型链上可枚举的属性))
var obj = {
a1:'我是第一个',
a2:'我是第er个',
a3:'我是第三个',
}
for(item in obj){ // item就是每一项
console.log(item, obj[item]) // a1 我是第一个 ...
}
Object.entries() 方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for..in 循环遍历对象返回顺序一致
let object = {
sex:'man'
age:'24'
}
console.log(Object.entries(object)) // [['sex','man'],['age','24']]
for(const [key,value] of Object.entries(object)){
console.log(key,value)
}
// sex man
// age 24
手写一个 new()
function mynew() {
var constr = Array.prototype.shift.call(arguments) // shift()从数组删除第一个元素,并返回该元素的值。此方法改变数组的长度
var obj = Object.create(constr.prototype)
var result = constr.apply(obj,arguments)
return result instanceof Object ? result : obj // insanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
}
function Person(name, age) {
this.name = name
this.age = age
}
const person1 = new Person('new啦啦', 18)
const person2 = mynew(Person, '自定义new啦啦', 17)
console.log(person1, person2)
手写 new 详细一点
function Person(name,age){
this.name=name
this.age=age
}
Person.prototype.sayHi=function(){
console.log('Hi!我是'+this.name)
}
let p1=new Person('张三',18)
////手动实现new
function create(){
let obj={}
//获取构造函数
let fn=[].shift.call(arguments) //将arguments对象提出来转化为数组,arguments并不是数组而是对象 !!!这种方法删除了arguments数组的第一个元素,!!这里的空数组里面填不填元素都没关系,不影响arguments的结果或者let arg = [].slice.call(arguments,1)
obj.__proto__=fn.prototype
let res=fn.apply(obj,arguments) //改变this指向,为实例添加方法和属性
//确保返回的是一个对象(万一fn不是构造函数)
return typeof res==='object'?res:obj
}
let p2=create(Person,'李四',19)
p2.sayHi()
function mynew(){
let constr = Array.prototype.shift.call(arguments)
let obj = Object.create(constr.pototype)
let res = constr.apply(obj,arguments)
return res instanceof Object ? res : obj
}
细节
[].shift.call(arguments) 也可写成:
let arg=[...arguments]
let fn=arg.shift() //使得arguments能调用数组方法,第一个参数为构造函数
obj.__proto__=fn.prototype
//改变this指向,为实例添加方法和属性
let res=fn.apply(obj,arg)
斐波那契(尾递归)
function fbnq(n){
if(n === 1 || n ===2){
return 1
}else{
fbna(n-2) + fbna(n-1)
}
}
function fbnq(n){
if(n === 1 || n === 2) return 1
return fabq(n-2)+fbna(n-1)
}
杂七乱八
try { // 只要这里代码有错误就会触发 catch 的语句 consolee.log('触发', money > pishi * shenti) } catch { console.log('meiyoubenshiba') } finally { console.log('我都是会触发,无论代码块有无出错!!!!') }
Array
常用的:slice、splice、spli
slice(start,end)截取返回新数组,且不改变原数组。
splice(start,delcount,item)
//start:起始位置
// deletecount:删除位数
// item:替换的item
// 返回值为被删除的字符串
// 如果有额外的参数,那么item会插入到被移除元素的位置上。
// splice:移除,splice方法从array中移除一个或多个数组,并用新的item替换它们。
split(separator,limit):split方法把这个string分割成片段来创建一个字符串数组。
// 可选参数limit可以限制被分割的片段数量。
// separator参数可以是一个字符串或一个正则表达式。
// 如果separator是一个空字符,会返回一个单字符的数组,不会改变原数组。
map() 方法会给原数组中的每个元素都按顺序调用一次 callback 函数,callback 函数会被自动传入三个参数:数组元素,元素索引,原数组本身。
let a = ['12','34','56']
const aaa = a.map((x)=>{
return parsenInt(x)
})
some() 方法用于检测数组中的元素是否满足指定条件(函数提供)如果有一个元素满足条件!!!,则表达式返回 true , 剩余的元素不会再执行检测!!!。如果没有满足条件的元素,则返回 false。
reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。可以作为一个高阶函数,用于函数的 compose。 注意: reduce() 对于空数组是不会执行回调函数的。四个参数 初始值, 或者计算结束后的返回值。当前元素,元素索引,元素所属数组
let a = [1,2,3]
let aaa = a.reduce((accumulator, currentValue)=>{
return accumulator+currentValue
})
aaa // 6
[1,2,3,4].reduce((x,y)=> console.log(x,y))
// 1,2 and undefined 3 and undefied 4
sort()方法对数组的元素进行排序到位并返回排序后的数组。默认排序顺序是升序,建立在将元素转换为字符串,然后比较它们的 UTF-16 代码单元值序列的基础上
函数比较数字,而不是字符串,则比较功能可以减去a2 从a。以下函数将按升序对数组进行排序
let a = [1,5,3,2,4]
a.sort((a1,a2)=> a1-a2) // 小到大
a.sort((a1,a2)=> a2-a1) // 从大到小
at(number) 方法接收一个整数值并返回该索引的项目,允许正数和负数。负整数从数组中的最后一个项目开始倒数。 find() 接受 callback 函数,函数带有 3 个参数:当前元素的值、当前元素的索引,以及数组本身。。数组中第一个满足所提供测试函数的元素的值,否则返回 undefined。
getter setter
get 获取 obj 的方法 set 去修改 obj 里面的值
将对象属性绑定到查询该属性时将被调用的函数(该函数不用传值!!!)
const obj = {
arr: [12,67,89,10],
get lastcount () {
if(this.arr.length === 0){
return 'undefined'
}
return this.arr[this.arr.length-1]
},
set addcount (x) {
this.arr.push(x)
}
}
console.log(obj.lastcount)
obj.addcpint = '添加值'
实现 JSON.parse()
function myparse(json){
return eval("(" + json + ")")
}
but 切记!!!!!!!eval() 是一个危险的函数, 它使用与调用者相同的权限执行代码。如果你用 eval() 运行的字符串代码被恶意方(不怀好意的人)修改,您最终可能会在您的网页/扩展程序的权限下,在用户计算机上运行恶意代码。更重要的是,第三方代码可以看到某一个 eval() 被调用时的作用域,这也有可能导致一些不同方式的攻击。相似的 Function 就不容易被攻击。
promise
三种状态:(只有三种状态,一旦开始不能改变(要么等待,要么resolve(成功),要么reject(失败)))
- 待定(Pening):初始状态,既没有被兑现,也没有拒绝
- 执行(Fulfilled):意味着操作成功(resolve())
- 拒绝(Rejected):意味着操作失败(reject())
Promise的方法
.then()方法 .catch()方法
一旦 Promise 被 resolve 或 reject,不能再迁移至其他任何状态(即状态 immutable(不可变)) 1、Promise.all([a1,a2]) // 多个请求放在 promise 里,返回每个成员的 promise 实例(都会返回值,只要是成功,其中一个失败就失败) 2、Promise.race([a1,a2]) // 多个实例,之中有一个实例率先改变状态,就先传递先改变的实例值给回调函数(拿最快返回的) 3、Promise.allSettled() // 适合所有异步操作都成功的情况,如果有一个操作失败,就无法满足要求
手写 promise
function myPromise(constructor){
let self=this;
self.status="pending" //定义状态改变前的初始状态
self.value=undefined;//定义状态为resolved的时候的状态
self.reason=undefined;//定义状态为rejected的时候的状态
function resolve(value){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.value=value;
self.status="resolved";
}
}
function reject(reason){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.reason=reason;
self.status="rejected";
}
}
//捕获构造异常
try{
constructor(resolve,reject);
}catch(e){
reject(e);
}
}
同时,需要在myPromise的原型上定义链式调用的then方法:
myPromise.prototype.then=function(onFullfilled,onRejected){
let self=this;
switch(self.status){
case "resolved":
onFullfilled(self.value);
break;
case "rejected":
onRejected(self.reason);
break;
default:
}
}
测试
var p=new myPromise(function(resolve,reject){resolve(1)});
p.then(function(x){console.log(x)})
//输出1
var p1 = new Promise((resolve,reject)=>{
// 做一些请求或别的判断
// resolve()返回成功
// reject()返回失败
})
深拷贝(是拷贝对象各个层级的属性)
简洁深拷贝:JSON.parse(JSON.stringify())
递归深拷贝:
function deepClone(obj){
let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
对象深拷贝注意
使用 JSON.parse() JSOM.stringify()的时候当值为 undefined、function、symbol 会在转换过程中被忽略
对象 Objet.assign() // 浅拷贝!!!
Object.assign({},sources)方法只会拷贝源对象自身的并且可枚举的属性到目标对象(如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性)。 Object.assign({}, ...sources){}定义一个空对象 / Object.assign(target, ...sources)也可目标对象,sources 源对象(要合并的对象,可多个)
数组深拷贝注意
let [... arr2] = arr // ...展开运算符 (数组包含对象||数组时就会失效,是没有把数组里面的对象深拷贝,而是浅拷贝)
let arr2 = arr.concat() // concat(array) 连接两个或多个数组返回新数组 (数组包含对象||数组时就会失效,是没有把数组里面的对象深拷贝,而是浅拷贝)
let arr2 = arr.slice(0) // slice(start,end) 从已有数组选定返回 (数组包含对象||数组时就会失效,是没有把数组里面的对象深拷贝,而是浅拷贝)
原型/原型链
原型
每个函数都有 prototype 属性(显式原型),这个属性指向函数的原型对象 每个对象(除了 null)都有proto 属性(隐式原型),指向该原型 每个原型都有 constructor 属性,指向该关联的构造函数
对象的原型只是一个引用,指向另一个对象。对象原型之间的嵌套组成了原型链,原型链的做用是维护访问对象属性的查询,肯定访问权限
原型链
拿对象的一个属性,但对象本身没有这个属性,就会去proto构造函数的对象中去寻找,如原型也没有就会一直proto往原型得原型上找,直到对象的原型对象是 null 结束寻找。形成链式解构 (原型链)
function Person() {
this.name ='飒飒';
this.age = 18
}
let newperson = new Person()
Person.prototype.sex = '我是男女!'
Person.prototype.age = 87
console.log(newperson.age) // 18
console.log(newperson.sex) // 我是男女,对象本身没有这个属性,__proto__去原型上找,原型没有返回undefined
console.log(Person.prototype) {sex: "你猜猜,我是男女!", age: 87, constructor: ƒ}
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的proto
Object.create() 与 Object.setPrototypeOf() 都是设置一个对象的原型对象 区别 在进行俩个原型之间的委托时使用 setPrototype 更好,Object.create 更适和直接对一个无原生原型的对象快速进行委托 Object.create() 凸显重新赋值, obj——> new prototype (直接赋值了新的原型) Object.setPrototypeOf() obj——> old prototype(本身原来的原型) ——>new prototype
Object.defineProperty()
备注:应当直接在 Object 构造器对象上调用此方法,而不是在任意一个 Object 类型的实例上调用。
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象
Object.defineProperty(obj, prop, descriptor) obj/目标对象 prop/要定义或修改的属性名 descriptor/要定义或修改的属性描述符,返回值:被传递给函数的对象。
let data = {}
let name = '三角龙'
Object.defineProperty(data,'name',{
get:function(){
return name
},
set:function(newvalue){ // 这里可以的到值得修改
if( name !== newvalue){
name = newvalue
}
return name
}
})
Proxy 代理拦截
vat proxy = new Proxy(target, handler)
用es6提供构造函数,生成proxy实例 target表示要拦截(包装)的目标对象(可任何类型的对象,包括原生数组、函数、甚至另一个代理), handler 参数也是对象,用来你制定的拦截行为(就是你拦截那个对象要干嘛)
let son = {
name: '小子',
age: 16,
sex: '男'
}
let proxy = new Proxy( son, {
get(target, key) {
return target[key]
},
<!-- set接受四个参数,1、Target用于接收属性的对象 2、key 要写入的“属性键”(我称为值得名字,可字符串或symbol)3、value 被写入的“属性值”(我称为写入的值)4、receiver:操作发生的对象(通常是代理)-->
set(target, key, value) {
// if(key === 'age' && typeof value !=='number') { //这里拦截了age不是number
// throw Error('年龄必须事number!')
// }
if(key === 'name' && value !=='小子') { //拦截不叫小子的
console.log('必须叫小子(proxy拦截)')
throw Error('必须叫小子')
}
return Reflect.set(target, key, value);
}
})
proxy.name = '假小子' // 这里就会触发代理拦截set里面我们自己定义判断的语句
let son = {
name:'小子',
age:26,
sex:'男',
desc:'这个是老子(grandson他爸爸)'
}
// 定义拦截行为
let validator {
get: function(obj,key){
return obj[key]
}
set: function(obj,name,value){
if(name === 'age' && value < 5) {
console.log('这小屁孩五岁都没有,拦他!!!(proxy拦截)')
throw Error('必须>五岁再说')
}
obj[name] = value
// 这里还可以赋值给别的对象(给son的对应值进行修改或赋值)
son[name] = value
return Reflect.set(obj,name,value)
}
}
// 定义proxy
let grandson = new Proxy({}, validator)
grandson.name = 'son的儿子'
grandson.age = 2 // 触发set定义的判断
vue 的 computed 计算属性和 wacth 侦听器
watch 顾名思义,用于监听数据变化,其中可以监听的数据来源有三部分:props、data、computed 内的数据;watch 提供两个参数(newValue,oldValue),第一个参数是新值,第二个参数旧值;(每次都会触发监听,不会有缓存)
computed 用于处理复杂的逻辑运算,主要和 methods 储存方法来进行区分;methods 储存方法,computed 储存需要处理的数据值;methods 每次都可调用,computed 有缓存机制,只有依赖值改变时才执行,性能更佳(有缓存,默认去 get,有变化才会触发 set);
watch: {
// wacthvalue这个在data中有定义,有变化就可以监听到做自己想要的处理
wacthvalue(newvalue,oldvalue){
console.log(newid,'东西',oldid)
// 代表在wacth里声明了firstName这个方法之后立即执行handler方法
immediate: true
}
}
computed:{
//computedMsg这个没有在data数据中没有定义的,页面直接this.computedMsg使用
computedMsg:{
get() { // 来取这里的值,赋值给computedMsg
return '我是data其他值来赋值或计算给computedMsg' // 可直接返回值
return this.xx + this.yy //也可以data里面的数据,这里xx,yy 有变化会触发
},
set(val) { // computedMsg值变化,这里就触发
console.log(val,'变化liao')
}
}
}
vuex(五个属性)
state moutations actions getters modules
state: 存放store值的
getters: 是store的计算属性(getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。)
moutations: 同步操作,直接修改state的值
SET_TOKEN: (state, token) => {
state.token = token
},
actions: 异步操作,通过commit用moutations的方法去修改state的值
commit('SET_TOKEN', res)
modules: 模块化,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
页面使用 actions 的方法修改 state 的值:this.$store.dispatch('某个模块/方法名',传参)
页面使用 moutations 的方法修改 state 的值:this.$store.commit("doLogout");
混入(mixin)
src 目录下新建一个 mixins 文件,里面有 index.js。组件使用(import 引入,mixins: [myMixin]注册、直接使用即可)
应该是共享方法,参数不是共享的 (必须引入才能使用)。也可以全局挂载去使用,方便处理后期改造
// mixins的index.js
export const myMixin = {
data(){ // 数据
return{
num:888
}
},
created(){ // 也有组件生命周期
// this.hello()
},
methods:{ // 方法
hello(){ // 组件有同名的方法,将被组件的方法覆盖
console.log('hello word!!!',this)
}
}
}
// 组件使用
import { myMixin } from "@/mixins/index.js";
mixins: [myMixin] // 直接在生命周期或方法中使用mixins index.js的方法或值
vuex 和 mixins 两者区别是,vuex 是全部组件都可以共享一个状态且都每个组件能进行修改,mixins 是每个组件都可以调用这个状态且都是独立的
vue vuex mixins 组件 wacth 计算属性 ?路由 插槽 自定义指令 $nextTick
$nextTick(在下次 Dom 更新循环结束之后执行延迟回调,在修改数据之后立即使用这个方法,获取更新后的 Dom)
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM
插槽
默认插槽(没有替换的,默认这个插槽)、具名插槽(没有这个插槽名字是不会显示)
作用域插槽(可以传子组件的值过去,作用域插槽的内部工作原理是将你的插槽内容包裹在一个拥有单个参数的函数里)、解构插槽(解构来传入具体的插槽 prop)
有一个父组件+子组件 父组件引入 使用 <子组件> 文本描述 </子组件> 子组件 就会替换为:文本描述
// 子组件
<template>
<slot>
默认插槽
</slot>
<slot name="mySlot">
具名插槽
</slot>
<slot name="myScope" myvalue="传递的值">
我是作用域插槽
</slot>
</template>
// 父组件
<template>
<子组件名>
// 默认插槽(写文本可以替换组件里的插槽文本,不写则默认组件里的文本)
<div>文本</div>
// 具名插槽,用tempalte v-solt后接具体名字即可。(default这个是默认值就是默认插槽)
<template v-solt:myScope>
替换文本
</template>
// 作用域插槽
<template v-slot:myScopr="slotProps">
作用域插槽的传值:{{slotProps.myvalue}}
</template>
<template #子组件名="xxx">
作用域插槽传值:{{xxx.myvalue}}
</template>
<template v-solt='{ 传值名 }'>
<div>解构插槽{{传值名.xxx}}</div>
</template>
</子组件名>
</template>
vue 组件
组件通过用 props 接收值,抽离出来一些公用的组件
vue 实例方法
this.$destroy() 完全销毁一个实例,清除它与其它实例的连接,解绑它的全部指令及事件监听器(触发 beforeDestroy 和 destroyed 钩子) v-if 在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建
vue3 setup 语法糖
组件通信
props(子接收,defineProps) $emit(子传&触发父事件) provide(父传)/inject(子接收) vuex
生命周期
setup 是在 beforeCreate、created 执行,其他生命周期 onBeforeMount、onMounted,onBeforeUpdate、onUpdated,
在 setup 里没有访问 this,且不能访问 this.route,代替用 useRouter 函数代替
组件引入后自动注册,不能像 vue2 那样定义对象值,要不然只能显示,修改后不会显示到 views 上。所以要用 ref 或 reactive
reactive
定义多个数据的响应式,接收一个普通对象然后返回该普通对象的响应式代理器对象(Proxy),响应式转换是“深层的”:会影响对象内部所有嵌套的属性, 我理解就是这里是定义包含多个响应值的对象
ref
定义一个响应式的数据(一般用来定义一个基本类型的响应式数据 Undefined、Null、Boolean、Number 和 String),返回的是一个 Ref 对象。操作数据时需要使用 ref 对象 value 属性进行操作(xxx.value),不会改变原始值 vue3 使用 ref 拿 dom 节点,现在标签定义 ref="xxx"然后定义一个 const xxx(跟标签那个一样名字) = ref(null)。‘xxx 名字’就是你要得 dom 节点 可以使用 ref 传值 子组件需要 defineExpose({ '传递的值' }),父组件调用就是:ref 的名字.传递的值即可
toRef && toRefs(我感觉这个好像就是一个浅拷贝的 api)
toRef(关联的对象,'单个属性')让其与响应数据关联起来,自己变化或关联变化都是一样的,引用会影响原始值,不会更新 UI
toRefs 将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref(延续响应对象所有属性),引用改变改变也会影响原始值
const obj = {name:'123'}
const newobj = toRef(obj,'name')
const newobj1 = toRefs(obj)
isRef 判断值是不是 ref 创建的数据
unref()如果参数是一个 ref,则返回内部值,否则返回参数本身。
vue3 Computed 与 watch
Computed 接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 ref 对象。
const plusOne = cpmputed(()=> count.value + 1) // plusOne就是计算的属性值
const PlusOne = computed({
get:()=> count.value +1, //PlusOne根据count这个值进行一些复杂运算
set:(val)=> { //PlusOne有变化触发这里
count.value = val -2
}
// set的运算了会再get运算,PlusOne根据count(依赖值)的变化进行运算
})
watch 两个参数,第一个是监听的数据对象(可以单个变量,数组,函数),第二个参数是数据改变时的回调函数, 有 2 个参数(新值,旧值), 第一个是改变后的数据, 第二个是改变前的数据;
watch(Parameter, (newvalue, oldvalue) => {
console.log(newvalue,oldvalue)
},{
// 监听的配置
immediate:true //一上来更新
})
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
ts
简单语法学习
/*
* @Author: Tangqiu
* @email: mound87@163.com
* @Date: 2022-01-25 16:29:09
*/
// 基本类型
let a:number = 123
let b:string = '我是字符串'
let c:number[] = [1,2,3,4]
let cc:string[] = ['1','2','3','4']
let cccc: Array<number> = [1,2,3]
let d:boolean = false
// any TypeScript类型系统的顶级类型,全局超级类型
// any 类型本质上是类型系统的一个逃逸舱。作为开发者,这给了我们很大的自由:TypeScript 允许我们对 any 类型的值执行任何操作,而无需事先执行任何形式的检查
let a1:any = 123
let b2:any = '我也是字符串'
let c3:any = [1,2,3,4,5]
let d4:any = false
// Unknown TypeScript类型系统的另一种顶级类型(比any更严格,用unknown定义的值不能赋值给其他类型除any外)
// unknown类型定义的值只能被赋值给 any 类型和 unknown 类型本身。直观地说,这是有道理的:只有能够保存任意类型值的容器才能保存 unknown 类型的值。毕竟我们不知道变量 value 中存储了什么类型的值。
let a5:unknown = 123
let b6:unknown = '我也也是字符串'
let c7:unknown = [1,2,3,4]
let d8:unknown = false
// Tuple类型(元组!!!)
// 元组可用于定义具有有限数量的未命名属性的类型。每个属性都有一个关联的类型。可利用push往数组添加其规定的类型,不受限数量
let tupletype : [string,boolean,number]
tupletype = ['我是字符串',false,123]
//Void类型 表示没有任何类型,当一个函数没有返回值时,通常会见到其返回值就是Void
// 声明一个 void 类型的变量没有什么作用,因为它的值只能为 undefined 或 null:
function myfunction() : void {
console.log('123321123321')
}
// TypeScript 断言
//尖括号语法 & as语法 (用来说明这个值详细信息:其实就是说它的类型)
let someValue: string = 'hellow word'
let someValueLength:number = (<string>someValue).length
let someValue1: any = 'hellow word'
let someValue1Length:number = (someValue1 as string).length
简单算法
两数之和 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那两个整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。
let towSun = function(nums,target){
let map = new Map()
for(let i = 0; i<nums.length; i++){
let tmp = target-nums[i]
if(map.has(tmp)){ // 判断是否存在map中
return [map.get(tmp),i] // 存在返回两个数的下标
}else{
map.set(nums[i],i) 不存在map中就将当前元素和下标存入map
}
}
return []
}
let towSun2 = function(nums,target){
let a = []
for(let i = 0; i<nums.length; i++){
let tmp = target-nums[i]
if(a[tmp] !== undefined) return [a[tmp],i]
a[nums[i]] = i
}
}