----- ES6数字操作
数字验证Number.isFinite( xx )
可以使用Number.isFinite( )来进行数字验证,只要是数字,不论是浮点型还是整形都会返回true,其他时候会返回false。
Number.isFinite(Number('123564')) //true Number.isFinite(Number('a')) //false Number.isFinite(Number(123)) //true
NaN验证
NaN是特殊的非数字,可以使用Number.isNaN()来进行验证。下边的代码控制台返回了true。
console.log(Number.isNaN(NaN)); //true
判断是否为整数Number.isInteger(xx)
let a=123.1,b=123 console.log(Number.isInteger(a)); //false console.log(Number.isInteger(b)); //true
整数转换Number.parseInt(xxx)和浮点型转换Number.parseFloat(xxx)
let a='9.18'; console.log(Number.parseInt(a)); //9 console.log(Number.parseFloat(a)); //9.18
整数取值范围操作
整数的操作是有一个取值范围的,它的取值范围就是2的53次方。我们先用程序来看一下这个数字是什么.
let a = Math.pow(2,53)-1; console.log(a); //9007199254740991
在我们计算时会经常超出这个值,所以我们要进行判断,ES6提供了一个常数,叫做最大安全整数,以后就不需要我们计算了。
最大安全整数
console.log(Number.MAX_SAFE_INTEGER); //9007199254740991
最小安全整数
console.log(Number.MIN_SAFE_INTEGER); //-9007199254740991
安全整数判断isSafeInteger( )
let a= Math.pow(2,53)-1; console.log(Number.isSafeInteger(a));//true
----- ES6循环
//输出key和value let arr = ['kevin','ke','keKevin']; for(let [index,val] of arr.entries()) { console.log(`${index}:${val}`) } //只输出key let arr = ['kevin','ke','keKevin']; for(let index of arr.keys()) { console.log(index) } //只输出value let arr = ['kevin','ke','keKevin']; for(let val of arr) { console.log(val) }
let arr = ['kevin','ke','keKevin']; let list = arr.entries(); console.log(list.next().value); //[0,"kevin"] console.log('-------------------------'); console.log(list.next().value); //[1,"ke"] console.log('*************************'); console.log(list.next().value); //[2,"keKevin"] console.log('6666666666666666666666666');
----- 数组遍历、替换,转字符串
// 数组遍历 let arr = ['kevin','ke','keKevin']; arr.forEach((val,index) => console.log(`${index}:${val}`)); //0:kevin 1:ke 2:keKevin arr.filter((val,index) => console.log(`${index}:${val}`)); //0:kevin 1:ke 2:keKevin arr.some((val,index) => console.log(`${index}:${val}`)); //0:kevin 1:ke 2:keKevin arr.map((val,index) => console.log(`${index}:${val}`)); //0:kevin 1:ke 2:keKevin // 替换 console.log(arr.map(x => 'web')) //["web", "web", "web"] // 数组转字符串 console.log(arr.join('')) //kevinkekeKevin
几种遍历方式比较
- for of 循环不仅支持数组、大多数伪数组对象,也支持字符串遍历,此外还支持 Map 和 Set 对象遍历。
- for in循环可以遍历字符串、对象、数组,不能遍历Set/Map
- forEach 循环不能遍历字符串、对象,可以遍历Set/Map
----- IOS和Android判断
var u = navigator.userAgent; var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //IOS var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端
----- 微信判断
function is_weixin() { let ua = window.navigator.userAgent.toLowerCase(); let flag = false; if (ua.match(/MicroMessenger/i) == 'micromessenger') { flag = true; } return flag; }
----- 字符串-对象转换
JSON.stringify(info); //对象转字符串 $.parseJSON(info); //字符串转对象
webpack代码部署index.html无内容显示
Tip: built files are meant to be served over an HTTP server. Opening index.html over file:// won't work. 解决:更改config/index.js 中的参数 assetsPublicPath:'./',
vue-cli项目中引入jquery
输入 npm install jquery --save-dev
用npm下载jq依赖、找到build文件夹下的webpack.base.conf.js文件打开,修改配置: 1、加入webpack对象: var webpack=require('webpack'); 2、在module.exports里面加入: plugins: [ new webpack.ProvidePlugin({ $:"jquery", jQuery:"jquery", "windows.jQuery":"jquery" }) ] 3、在入口文件main.js中加入 import $ from 'jquery' 全局引入jq
常用的数组函数
// 1、push()向Array的末尾添加若干元素,pop()则把Array的最后一个元素删除掉 var arr = [1, 2]; arr.push('A', 'B'); // 返回Array新的长度: 4 arr; // [1, 2, 'A', 'B'] arr.pop(); // pop()返回'B' arr; // [1, 2, 'A'] arr.pop(); // 空数组继续pop不会报错,而是返回undefined arr; // []: // 2、往Array的头部添加若干元素,使用unshift()方法,shift()方法则把Array的第一个元素删掉 var arr = [1, 2]; arr.unshift('A', 'B'); // 返回Array新的长度: 4 arr; // ['A', 'B', 1, 2] arr.shift(); // 'A' arr; // ['B', 1, 2]: // 3、reverse反转 var arr = ['one', 'two', 'three']; arr.reverse(); arr; // ['three', 'two', 'one'] // 4、splice()方法是从指定的索引开始删除若干元素,然后再从该位置添加若干元素 var arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle']; // 从索引2开始删除3个元素,然后再添加两个元素: arr.splice(2, 3, 'Google', 'Facebook'); arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle'] // 5、concat()方法连接两个数组 var arr = ['A', 'B', 'C']; var added = arr.concat([1, 2, 3]); added; // ['A', 'B', 'C', 1, 2, 3] // 6、join方法将数组以指定字符串连接,返回连接后的字符串 var arr = ['A', 'B', 'C', 1, 2, 3]; arr.join('-'); // 'A-B-C-1-2-3' // 7、filter返回符合条件的元素 var arr = ['A', 'B', 'C']; arr.filter((item) => {item!='A'}); console.log(arr.filter((item) => item!='A')) // ['B','C']
检测类型
var a = 'kevin'; var b = true; var c = 22; var d; var e = null; var f = new Object(); alert(typeof a) ; //string alert(typeof b) ; //boolean alert(typeof c) ; //number alert(typeof d) ; //undefined alert(typeof e) ; //object alert(typeof f) ; //object alert(a instanceof Object) //变量a是Object吗? alert(b instanceof Array) //变量a是Array吗? alert(c instanceof RegExp) //变量a是RegExp吗?
父子组件通信
// 1、父组件通过prop属性传值给子组件 --父组件 <child :message="_data"></child> new Vue({ el: 'app', data: { _data: 'Message from parent' } }) Vue.component('child', { // 声明 props props: ['message'], // 就像 data 一样,prop 也可以在模板中使用 // 同样也可以在 vm 实例中通过 this.message 来使用 template: '<span>{{ message }}</span>' })
// 2、子组件通过自定义事件和$emit触发事件,改变父组件变量值 <div id="counter-event-example"> <p>{{ total }}</p> <button-counter v-on:increment="incrementTotal"></button-counter> <button-counter v-on:increment="incrementTotal"></button-counter> </div> Vue.component('button-counter', { template: '<button v-on:click="incrementCounter">{{ counter }}</button>', data: function () { return { counter: 0 } }, methods: { incrementCounter: function () { this.counter += 1 this.$emit('increment') } }, }) new Vue({ el: '#counter-event-example', data: { total: 0 }, methods: { incrementTotal: function () { this.total += 1 } } })
详情:https://cn.vuejs.org/v2/guide/components.html#Prop
=>
5个迭代方法
ps:5个迭代方法都不会改变原数组 every 返回值:Boolean;含义:如果数组中每个元素均满足条件,则返回true,否则返回false。 some 返回值:Boolean;含义:如果数组中有某个元素满足条件,则返回true,否则返回false。 filter 返回值:Array;含义:返回原数组中满足条件的元素组成的新数组。 map 返回值:Array;含义:返回原数组的每一项经过map参数指定的函数处理后的新数组。 forEach 返回值:没有返回值;含义:对数组中的每一项应用forEach参数指定的函数。 //其中item是每一项,index是索引,array是原数组 var num = [1,2,3,4,5,4,3,2,1]; var every = num.every(function(item,index,array){return item>2})
2个归并方法
reduce()和reduceRight() 这两个方法都接收两个参数:一个在每一项上调用的函数和(可选的)作为归并基础的初始值。传给reduce()和reducnRight()的函数接收4个参数:前一个值,当前值,项的索引和数组对象。
这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项。 var val = [1,2,3,4,5]; var sum = val.reduce(function(pre,cur,index,array){ return pre+cur; }) alert(sum) //15 reduceRight()和reduce()类型,只是方向相反而已
闭包
--闭包就是一个函数引用另外一个函数的变量,因为变量被引用着所以不会被回收,因此可以用来封装一个私有变量。这是优点也是缺点,不必要的闭包只会徒增内存消耗! --闭包其实很简单,根本上还是变量解析。而之所以可以实现,还是因为变量解析会在作用域链中依次寻找对应属性的导致的。 function createFunctions(){ var result = new Array(); for (var i=0; i < 10; i++){ result[i] = function(){ return i; }; } return result; } var funcs = createFunctions(); // 这里并没有执行函数,此时 i=10,funcs此时是一个有10个函数的数组 // funcs=[f(){console.log(i)},f(){console.log(i)},...] for (var i=0; i < funcs.length; i++){ console.log(funcs[i]()); } // 输出10个10
js和jq获取data属性
js获取data-*的方式 通过dataset属性访问 <div id="myDiv" data-appid="123" data-myname="lsxj"></div> var div = document.getElementById("myDiv"); var appId = div.dataset.appid;//获取data-appid的值 var myName = div.dataset.myname;//获取data-myname的值 //设置值 div.dataset.appid = 456; div.dataset.myname = "newname"; //最终HTML结果 <div id="myDiv" data-appid="456" data-myname="newname"></div> dataset属性的值是DOMStringMap的一个实例,名值对的映射。每个data-name形式的属性都有一个对应的属性,只不过该属性名没有data-前缀。
data()方法 //HTML代码
<div id="myDiv" data-appid="123" data-myname="lsxj" data-app-id="456" data-my-name="secondname"></div> //获取属性 var appid = $("#myDiv").data("appid"); //123 var app-id = $("#myDiv").data("app-id"); //456 //属性赋值 $("#myDiv").data("appid","666"); //最终HTML代码 <div id="myDiv" data-appid="123" data-myname="lsxj" data-app-id="456" data-my-name="secondname"></div> //需要注意的是,data()的值进行修改并不会影响到DOM元素上的data-*属性的改变。data()的本质其实是将一个 “cache” 附加到了对象上,并使用了一个特殊的属性名称。 //所以上述代码中,虽然对div进行了data()赋值操作,但HTML代码中div的data-appid的值仍然为123,因为data()只是修改了缓存的那个值,此时进行$('#myDiv').data("appid")的操作,输出的结果为666. attr()方法 var appid = $("#myDiv").attr("data-appid");
prop和attr
对于HTML元素本身就带有的固有属性,在处理时,使用prop方法。
对于HTML元素我们自己自定义的DOM属性,在处理时,使用attr方法。
事件委托
Event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置,并不知道是什么节点名称,这里我们用nodeName来获取具体是什么标签名,这个返回的是一个大写的,我们需要转成小写再做比较(习惯问题):
window.onload = function(){ var oUl = document.getElementById("ul1"); oUl.onclick = function(ev){ var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLowerCase() == 'li'){ alert(123); alert(target.innerHTML); } } }
js追加元素的几种方法(append()、prepend()、after()、before()、insertAfter()、insertBefore())
$(function(){ //append(),在父级最后追加一个子元素 $(".append").click(function(){ $("#wrap").append("<p class='three'>我是子元素append</p>"); }); //appendTo(),将子元素追加到父级的最后 $(".appendTo").click(function(){ $("<p class='three'>我是子元素appendTo</p>").appendTo($("#wrap")); }); //prepend(),在父级最前面追加一个子元素 $(".prepend").click(function(){ $("#wrap").prepend("<p class='three'>我是子元素prepend</p>"); }); //prependTo(),将子元素追加到父级的最前面 $(".prependTo").click(function(){ $("<p class='three'>我是子元素prependTo</p>").prependTo($("#wrap")); }); //after(),在当前元素之后追加(是同级关系) $(".after").click(function(){ $("#wrap").after("<p class='siblings'>我是同级元素after</p>"); }); //before(),在当前元素之前追加(是同级关系) $(".before").click(function(){ $("#wrap").before("<p class='siblings'>我是同级元素before</p>"); }); //insertAfter(),将元素追加到指定对象的后面(是同级关系) $(".insertAfter").click(function(){ $("<p class='three'>我是同级元素insertAfter</p>").insertAfter($("#wrap")); }); //insertBefore(),将元素追加到指定对象的前面(是同级关系) $(".insertBefore").click(function(){ $("<p class='three'>我是同级元素insertBefore</p>").insertBefore($("#wrap")); }); }); //appendChild(),在节点的最后追加子元素 function appChild(){ // 创建p节点 var para=document.createElement("p"); // 创建文本节点 var node=document.createTextNode("我是子集appendChild新段落。"); // 把文本节点添加到p节点里 para.appendChild(node); // 查找div1 var element=document.getElementById("wrap"); // 把p节点添加到div1里 element.appendChild(para); }
对象深浅拷贝(方法还有很多种,譬如还有使用JSON.stringify进行序列化,JSON.parse进行反序列化,实现"偷懒版"的深复制...等等)
//浅拷贝只复制一层
var deepCopy= function(source) {
var result={}; for (var key in source) { result[key] = source[key]; } return result; }
//深拷贝 var deepCopy= function(source) { var result={}; for (var key in source) { //理应除去null result[key] = typeof source[key]===’object’? deepCoyp(source[key]): source[key]; } return result; }
从浏览器的URL中获取查询字符串参数
function GetQueryString(name){ var reg = new RegExp("(^|/?/?)"+ name +"=([^/?/?]*)(/?/?|$)"); var r = window.location.search.substr(1).match(reg); if(r!=null)return decodeURI(r[2]); return null; }
// 获取url字段 function GetRequest() { var url = location.href; //获取url中"?"符后的字串 var theRequest = new Object(); if (url.indexOf("?") != -1) { var str = url.split("?")[1]; let strs = str.split("&"); for (var i = 0; i < strs.length; i++) { theRequest[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]); } } return theRequest; } const params = GetRequest(); if(params.appId){ localStorage.setItem('h5_appKey',decodeURIComponent(params.appId)) }
简单透彻理解JSONP原理及使用
获取url字符串
// 正则 function GetQueryString(name){ var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)"); var r = window.location.search.substr(1).match(reg); if(r!=null)return decodeURI(r[2]); return null; } // 普通 function showWindowHref(){ var sHref = window.location.href; var args = sHref.split('?'); if(args[0] == sHref){ return ""; } var arr = args[1].split('&'); var obj = {}; for(var i = 0;i< arr.length;i++){ var arg = arr[i].split('='); obj[arg[0]] = arg[1]; } return obj; } var href = showWindowHref(); // obj console.log(href['name']); // xiaoming
验证文件类型demo
uploadimg: function(e) { var file = e.target.files[0]; var that = this var img = ''; if (!/image\/\w+/.test(file.type)) { $.toast('文件必须为图片'); return false; } var reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function (res) { that.src = res.target.result; } }
清空file
const file = document.getElementById('file');
file.outerHTML = file.outerHTML;
------ CSS杂乱知识 ------
选择器权重
important > 内联 > ID > class > 标签 | 伪类 | 属性选择器 > 伪对象 > 继承 > 通配符
行内元素可以设置margin和padding,但上下margin和上padding不生效
步骤:
1、复制已经登录的链接:http://192.168.33.9:8300/blackbox-web-pc/#/crowdSelect/user
2、重新打开一个新的谷歌浏览器,粘贴链接,回车;
预期:
界面回到登录界面,需要用户重新登录;
用window.sessionStorage.getItem() 去判断
文字超出部分隐藏并加上...
.overflow {
// overflow: hidden;
// text-overflow: ellipsis;
// display: -webkit-box;
// -webkit-box-orient: vertical;
// -webkit-line-clamp: 2; //设置多行, 火狐不兼容
overflow: hidden; //以下兼容火狐,一行超出隐藏
white-space: nowrap;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
}
将xml转json,在头部加入以下代码
Accept: 'application/json'
es6数组去空
defList.filter(x => true)
es6数组去重
[...new Set([1,2,5,1,3])]
set增删查
let setArr = new Set(['jspang','技术胖','web','jspang']); console.log(setArr);//Set {"jspang", "技术胖", "web"} setArr.add('前端职场'); //添加 console.log(setArr); //Set {"jspang", "技术胖", "web", "前端职场"} setArr.delete('前端职场'); //删除 console.log(setArr); //Set {"jspang", "技术胖", "web"} // 查询 console.log(setArr.has('jspang'));//true // 清除 setArr.clear(); console.log(setArray);//true
ES6中Set和WeakSet的使用 -- 描述有一些错误,详细见阮一峰ES6
Set实现并集,交集,差集
let set1 = new Set([1,2,3,4,5,6]); let set2 = new Set([4,5,6,7,8,9]); //并集 let union = new Set([...set1,...set2]); //[1,2,3,4,5,6,7,8,9] //交集 let intersect = new Set([...set1].filter(x => set2.has(x))); //[4,5,6] //差集 let reduce= new Set([...set1].filter(x => !set2.has(x))); //[1,2,3] console.log(union ,intersect ,reduce)
关于前端静态方法、实例方法、私有方法的区别
1. 静态方法 // 创建一个类 var BuriaPoint = fuction(){ } // 定义xxx静态方法 BuriaPoint.xxx = function(){ } 使用时,直接 BuriaPoint.xxx(); 2. 实例方法 // 创建一个类 var BuriaPoint = fuction(){ } // 定义xxx实例方法 BuriaPoint.prototype.xxx = function(){ } 使用时,需要new一个实例 new BuriaPoint().xxx(); 3. 私有方法 // 创建一个类 var BuriaPoint = fuction(){ var xxx = function(){ } // 只能在类内部使用 xxx(); }
/ 创建一个类,有两种办法,一个是原型链(上面已有),一个构造函数如下: // 构造函数创建一个类 var Student = function(props){ this.a = props.x; this.b = props.y; this.xxx = function(){ console.log(this.a); } } new Student({ x: 20, y: 30 }).xxx();
一键复制文本到粘贴板
// 复制 const copy = () => { let Url2 = document.getElementById("js-copy-text"); Url2.select(); // 选择对象 try { if (document.execCommand('copy', false, null)) { document.execCommand("Copy"); message.success("已复制好,可贴粘。"); } else { message.error("复制失败,请手动复制"); } } catch (err) { message.error("复制失败,请手动复制"); } }
<Col className={cx(styles.optionBorder)}> <Input value={finishData.shortUrl} id='js-copy-text' /> </Col> <Col className={cx(styles.fz1)} onClick={copy}>复制链接</Col>
ES6 一共有 5 种方法可以遍历对象的属性。
(1)for...in
for...in
循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。
(2)Object.keys(obj)
Object.keys
返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
(3)Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames
返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
(4)Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols
返回一个数组,包含对象自身的所有 Symbol 属性的键名。
(5)Reflect.ownKeys(obj)
Reflect.ownKeys
返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。
- 首先遍历所有数值键,按照数值升序排列。
- 其次遍历所有字符串键,按照加入时间升序排列。
- 最后遍历所有 Symbol 键,按照加入时间升序排列。
Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 }) // ['2', '10', 'b', 'a', Symbol()]
上面代码中,Reflect.ownKeys
方法返回一个数组,包含了参数对象的所有属性。这个数组的属性次序是这样的,首先是数值属性2
和10
,其次是字符串属性b
和a
,最后是 Symbol 属性。
对象遍历键值
var item = {key: 'key1',name: 'name1'} Object.keys(item).forEach(function (key, k, v) { console.log(key,k,v) }) //key 0 ["key", "name"] //name 1 ["key", "name"]
let {keys, values, entries} = Object; let obj = { a: 1, b: 2, c: 3 }; for (let key of keys(obj)) { console.log(key); // 'a', 'b', 'c' } for (let value of values(obj)) { console.log(value); // 1, 2, 3 } for (let [key, value] of entries(obj)) { console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3] }
dva知识
State:一个对象,保存整个应用状态
View:React 组件构成的视图层
Action:一个对象,描述事件
connect 方法:一个函数,绑定 State 到 View
dispatch 方法:一个函数,发送 Action 到 State
//供应商 let obj = { time: '2017-08-31', name: 'Tim', _r: 123 }; //代理商 let monitor = new Proxy(obj, { //代理读取对象属性 get(target, key,value) { //当读取对象属性时,将所有2017替换成2018 return target[key].replace('2017', '2018') }, //代理设置对象属性 set(target, key, value) { //只有key值是name的时候,才允许修改;否则返回原来的值。 if (key === 'name') { return target[key] = value; } else { return target[key]; } }, //拦截key in object操作 has(target, key) { //只有key值是name的时候,才返回true;否则返回false。 if (key === 'name') { return target[key]; } else { return false; } }, //拦截删除操作 deleteProperty(target, key) { //只有key值以“_”开头,才允许删除。 if (key.indexOf('_') != -1) { delete target[key]; return true; } else { return target[key] } }, //拦截Object.keys, Object.getOwnPropertySymbols, Object.getOwnPropertyNames ownKeys(target) { //使用数组的filter方法过滤掉time属性,不暴露出来 return Object.keys(target).filter(item => item != 'time') } } ) //读取 console.log('读取操作', monitor.time); //2018-08-31 //设置 monitor.time = '2019'; //设置失败 monitor.name = 'Chen'; //设置成功 console.log('设置操作', monitor.time, monitor.name); //2018-08-31 Chen //has in object操作 console.log('has', 'name' in monitor); // true console.log('has', 'time' in monitor); // false //删除操作 delete monitor.time; //并没有成功 console.log('删除操作', monitor); //time: '2017-08-31', name: 'Tim', _r: 123 //遍历属性操作 console.log(Object.keys(monitor)); // name, _r
reflect实例
let obj = { time: '2017-08-31', name: 'Tim', _r: 123 }; //get读取操作 console.log('Reflect get读取',Reflect.get(obj,'time')); //set设置操作 Reflect.set(obj,'name','CCC'); console.log(obj); // time: '2017-08-31', name: 'CCC', _r: 123 //has操作,相当于以前的key in obj console.log('Reflect has',Reflect.has(obj,'name')); // true //删除 const myObj = { foo: 'bar' }; Reflect.deleteProperty(myObj, 'foo'); //{}
Proxy和Reflect配合使用
{ //数据校验主程序 function validator(target,validator){ return new Proxy(target,{ _validator: validator, set(target,key,value,proxy){ //判断当前对象是否有这个key值 if(target.hasOwnProperty(key)){ //执行校验条件函数 let va = this._validator[key]; //是否符合条件 if(!!va(value)){ return Reflect.set(target,key,value,proxy) } else{ throw Error(`不能设置${key}为${value}`) } } else{ throw Error(`${key} 不存在`) } } }) } //校验条件函数 const personValidators = { name(val){ return typeof val === 'string' }, age(val){ return typeof val === 'number' && val > 18 } } //类 class Person{ constructor(name,age){ this.name = name; this.age = age; return validator(this,personValidators) } } const person = new Person('xiaoming',30); console.log(person); // {name: 'xiaoming' , age: 30} person.name=38; // 设置失败,因为name需要是一个字符串 person.age=38; // 设置成功 }
下面是 Proxy 支持的拦截操作一览,一共 13 种。 get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo']。 set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。 has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。 deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。 ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。 getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。 defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。 preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。 getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。 isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。 setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。 apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。 construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
对象克隆
var kk = { ll: { oo: 444 }, pp: 666 } var clone = Object.assign({},kk) //相互独立
绑定事件
绑定事件 document.querySelector('body').addEventListener('click', handleClick, false); // 移除事件 document.querySelector('body').removeEventListener('click', handleClick, false);
高效遍历匹配Json数据,避免嵌套循环
工作中经常会遇到这样的需求:
1.购物车列表中勾选某些,点击任意一项,前往详情页,再返回购物车依旧需要呈现勾选状态
2.勾选人员后,前往别的页面,再次返回,人员依旧程勾选状态
3.等等....
数据结构如下:
// 缓存数据 var students = [ { id: 35, name: '小明', age: 25, address: '环球中心',checked:true}, { id: 36, name: '杰伦', age: 41, address: '中国台湾' ,checked:true}, { id: 37, name: '不撸死', age: 46, address: '霉国' ,checked:true} ] // 最新数据 var data = [ { id: 35, name: '小明', age: 25, address: '环球中心',checked:false }, { id: 36, name: '杰伦', age: 41, address: '中国台湾' ,checked:false}, { id: 37, name: '不撸死', age: 46, address: '霉国' ,checked:false}, { id: 38, name: '大明', age: 46, address: '哈哈哈哈哈' ,checked:false}, { id: 39, name: '中明', age: 46, address: '中国四川' ,checked:false} ]
思路如下:
离开页面的时候将勾选的数据缓存,再次返回到页面时,将最新添加的数据和缓存的数据做对比,如果缓存中存在勾选,则更改对应的最新数据。
在做数据比对的时候,可以通过嵌套for循环,一层for循环遍历最新数据,二层for循环遍历缓存数据,如果缓存数据中对应的checked为true,则更改第一层for循环对应的值。虽然通过嵌套循环可以实现效果,但是循环的次数是两个数组长度的乘积,当数据量大的时候,这样会很耗性能。 这里推荐另一种办法,将缓存的数组转换成Json对象
,将唯一的id
作为数组中每一项的key
,将数组的每一项做为value
,这样循环的时候只需要一层循环即可。
具体代码如下:
// 缓存数据 var students = [ { id: 35, name: '小明', age: 25, address: '环球中心',checked:true }, { id: 36, name: '杰伦', age: 41, address: '中国台湾' ,checked:true}, { id: 37, name: '不撸死', age: 46, address: '霉国' ,checked:true} ] // 最新数据 var data = [ { id: 35, name: '小明', age: 25, address: '环球中心',checked:false }, { id: 36, name: '杰伦', age: 41, address: '中国台湾' ,checked:false}, { id: 37, name: '不撸死', age: 46, address: '霉国' ,checked:false}, { id: 38, name: '大名', age: 46, address: '哈哈哈哈哈' ,checked:false}, { id: 39, name: '中明', age: 46, address: '中国四川' ,checked:false} ] // 将数组转换为json对象 function Array2Json(arr, obj = {}) { arr.forEach(item => { obj[item.id] = item; }) return obj } students = Array2Json(students); // 此处可以用for循环,但是推荐使用while,因为while比for效率高 let i = 0; while (i < data.length) { if (students[data[i].id]) { data[i].checked = true } i++; } // 最终得到的data就是还原了勾选状态的数据,可以直接渲染在界面上 console.log(data)
网页中预加载20张图片资源,分步加载,一次加载10张,两次完成,怎么控制图片请求的并发,怎样感知当前异步请求是否已完成?
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>单一请求 - 多个 Promise 同步化</title> <style> .pics{ width: 300px; margin: 0 auto; } .pics img{ display: block; width: 100%; } .loading{ text-align: center; font-size: 14px; color: #111; } </style> </head> <body> <div class="wrap"> <div class="loading">正在加载...</div> <div class="pics"> </div> </div> <script> (function () { const urls = ['./images/1.jpg', './images/2.jpg', './images/3.jpg', './images/4.jpg'] syncLoad(loadImg, urls, addToHtml) .then(() => { document.querySelector('.loading').style.display = 'none' }) .catch(console.log) function syncLoad (fn, arr, handler) { if (typeof fn !== 'function') throw TypeError('第一个参数必须是function') if (!Array.isArray(arr)) throw TypeError('第二个参数必须是数组') handler = typeof fn === 'function' ? handler : function () {} const errors = [] return load(0) function load (index) { if (index >= arr.length) { return errors.length > 0 ? Promise.reject(errors) : Promise.resolve() } return fn(arr[index]) .then(data => { console.log(data,'data') handler(data) }) .catch(err => { console.log(err) errors.push(arr[index]) return load(index + 1) }) .then(() => { return load (index + 1) }) } } function loadImg (url) { return new Promise((resolve, reject) => { const img = new Image() img.onload = function () { resolve(img) } img.onerror = reject img.src = url }) } function addToHtml (img) { document.querySelector('.wrap .pics').appendChild(img) } })() </script> </body> </html>
对象排序
// 对象排序 export function objKeySort(arys, flag) { // 先用Object内置类的keys方法获取要排序对象的属性名,再利用Array原型上的sort方法对获取的属性名进行排序,newkey是一个数组 let newkey if (flag === 0) { newkey = Object.keys(arys).sort(compare) } else if (flag === 1) { newkey = Object.keys(arys).sort() } // console.log('newkey='+newkey); var newObj = {} // 创建一个新的对象,用于存放排好序的键值对 for (let i = 0; i < newkey.length; i++) { // 遍历newkey数组 newObj[newkey[i]] = arys[newkey[i]] // 向新创建的对象中按照排好的顺序依次增加键值对 } return newObj // 返回排好序的新对象 } function compare(value1, value2) { if (value1 < value2) { return 1 } else if (value1 > value2) { return -1 } else { return 0 } } // 使用 var arr = { aaa: 1111, bbb: 2222, } objKeySort(arr,1) //升序 objKeySort(arr,0) //降序