开发中,遇到数组排序的需求很频繁,这篇文章会介绍几个常见排序思路。
如果要从大到小排列,则 while(arr[n] > arr[n - interval] && n > 0)
。
// 希尔排序算法 function xier(arr){ var interval = parseInt(arr.length / 2);//分组间隔设置 while(interval > 0){ for(var i = 0 ; i < arr.length ; i ++){ var n = i; while(arr[n] < arr[n - interval] && n > 0){ var temp = arr[n]; arr[n] = arr[n - interval]; arr[n - interval] = temp; n = n - interval; } } interval = parseInt(interval / 2); } return arr; } // Array var arr = [10, 20, 40, 60, 60, 0, 30] // 打印排序后的数组 console.log(xier(arr))//[0, 10, 20, 30, 40, 60, 60]
一堆数组排序
// Array var arr = [10, 20, 40, 60, 60, 0, 30] // 排序方法 arr.sort(function(a,b){ /*
* return b-a; —> 降序排序
* return a-b; —> 升序排列
*/ return a-b; })//括号里不写回调函数则默认按照字母逐位升序排列 // 打印排序后的数组 console.log(arr)//[0, 10, 20, 30, 40, 60, 60]
对象数组排序(数组套对象)
//对象数组排序 var arr = [ {name:'syy', age:0}, {name:'wxy', age:18}, {name:'slj', age:8}, {name:'wj', age:20} ]; // 排序方法 function compare(property) {//property:根据什么属性排序 return function(a,b){ var value1 = a[property]; var value2 = b[property]; /*
* value2 - value1; ——> 降序
* value1 - value2; ——> 升序
*/ return value1 - value2;//升序排序 } } // 打印排序后的数组 console.log(arr.sort(compare('age'))) /*
0: {name: "syy", age: 0}
1: {name: "slj", age: 8}
2: {name: "wxy", age: 18}
3: {name: "wj", age: 20}
*/
特点:简单,但非常浪费内存,几乎不用。
桶中出现的数组元素都做个标记 1
,然后将桶数组中有 1
标记的元素依次打印。
// Array var arr = [] // 每个数组项做标记(1) for(let i = 0; i < arr.length; i++) { let key = arr[i] arr[key] = 1 } // 遍历打印出每项 for(let j in arr) { debugger console.log(j) }
性能:一般(需要每项进行比较)。
每一趟找出最大的值。
// Array var arr = [10, 20, 40, 60, 60, 0, 30] /*
* 总共比较次数为arr.length-1次
* 每次的比较次数为arr.length-1次
* 依次递减
*/ var temp;//交换变量标识 // 两层for分别表示当前项与第二项 for(let i = 0; i < arr.length - 1; i++) { for(let j = 0; j < arr.length - 1; j++) { // 如果当前项大于第二项(后一项)则交换 if(arr[j] > arr[j+1]) { temp = arr[j] arr[j] = arr[j+1]; arr[j+1] = temp; } } } // 打印排序后的数组 console.log(arr)//[0, 10, 20, 30, 40, 60, 60]
性能:一般(需要每项进行比较)。
假定某个位置的值是最小值,与冒泡排序类似。
// Array var arr = [10, 20, 40, 60, 60, 0, 30] var temp;//交换变量标识 // 两层for分别表示当前项与第二项 for(let i = 0; i < arr.length - 1; i++) { for(let j = i + 1; j < arr.length; j++) { // 假设第二项是最小值(是则交换/否则继续比较) if(arr[i] > arr[j]) { temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } // 打印排序后的数组 console.log(arr)//[0, 10, 20, 30, 40, 60, 60]
// Array var arr = [10, 20, 40, 60, 60, 0, 30] // 排序算法 for(var i = 0; i < arr.length; i++) { var n = i; while(arr[n] > arr[n+1] && n >= 0) { var temp = arr[n]; arr[n] = arr[n+1]; arr[n+1] = temp; n--; } } // 打印排序后的数组 console.log(arr)//[0, 10, 20, 30, 40, 60, 60]
作者:王佳斌
了解拷贝背后的过程,避免不必要的错误,Js专题系列之深浅拷贝,我们一起加油~
当我们在操作数据之前,可能会遇到这样的情况:
当我们遇到类似需要场景时,首先想到的就是拷贝它,殊不知拷贝也大有学问哦~
下面简单的例子,你是否觉得熟悉?
var str = 'How are you'; var newStr = str; newStr = 10 console.log(str); // How are you console.log(newStr); // 10
大家都能想到,字符串是基本类型,它的值保存在栈中,在对它进行拷贝时,其实是为新变量开辟了新的空间。 str
和newStr
就好比两个一模一样的房间,布局一致却毫无关联。
var data = [1, 2, 3, 4, 5]; var newData = data; newData[0] = 100; console.log(data[0]); // 100 console.log(newData[0]); // 100
类似的代码段,但这次我们使用数组这个引用类型举例,你会发现修改赋值后的数据,原始数据也跟着改变了,这显然不满足我们的需要。本篇文章就来聊一聊引用数据拷贝的学问。
如果大家对Js的数据类型存在着疑问,不妨看看《JavaScript中的基本数据类型》
拷贝的划分都是针对引用类型来讨论的,浅拷贝——顾名思义,浅拷贝就是“浅层拷贝”,实际上只做了表面功夫:
var arr = [1, 2, 3, 4]; var newArr = arr; console.log(arr, newArr); // [1,2,3,4] [1,2,3,4] newArr[0] = 100; console.log(arr, newArr) // [100,2,3,4] [100,2,3,4]
不发生事情(操作)还好,一旦对新数组进行了操作,两个变量中所保存的数据都会发生改变。
发生这类情况的原因也是因为引用类型
的基本特性:
数组中的slice和concat都会返回一个新数组,我们一起来试一下:
var arr = [1,2,3,4]; var res = arr.slice(); // 或者 res = arr.concat() res[0] = 100; console.log(arr); // [1,2,3,4]
这个问题这么快就解决了?虽然对这一层数据进行了这样的的处理后,确实解决了问题,但!
var arr = [ { age: 23 }, [1,2,3,4] ]; var newArr = arr.concat(); arr[0].age = 18; arr[1][0] = 100; console.log(arr) // [ {age: 18}, [100,2,3,4] ] console.log(newArr) // [ {age: 18}, [100,2,3,4] ]
果然事情没有那么简单,这也是因为数据类型的不同。
S 不允许我们直接操作内存中的地址,也就是说不能操作对象的内存空间,所以,我们对对象的操作都只是在操作它的引用而已。
既然浅拷贝
达不到我们的要求,本着效率的原则,我们找找有没有帮助我们实现深拷贝
的方法。
数据的方法失败了,还有没有其他办法?我们需要实现真正意义上的拷贝出独立的数据。
这里我们利用JSON的两个方法,JSON.stringify()
,JSON.parse()
来实现最简洁的深拷贝
var arr = ['str', 1, true, [1, 2], {age: 23}] var newArr = JSON.parse( JSON.stringify(arr) ); newArr[3][0] = 100; console.log(arr); // ['str', 1, true, [1, 2], {age: 23}] console.log(newArr); // ['str', 1, true, [100, 2], {age: 23}]
这个方法应该是实现深拷贝最简洁的方法,但是,它仍然存在问题,我们先来看看刚才都做了些什么:
arr
JSON 字符串
值或对象
理解:
我们可以理解为,将原始数据转换为新字符串
,再通过新字符串
还原为一个新对象
,这中改变数据类型的方式,间接的绕过了拷贝对象引用的过程,也就谈不上影响原始数据。
限制:
这种方式成立的根本就是保证数据在“中转”时的完整性,而JSON.stringify()
将值转换为相应的JSON格式
时也有缺陷:
所以当我们拷贝函数、undefined等stringify
转换有问题的数据时,就会出错,我们在实际开发中也要结合实际情况使用。
举一反三:
既然是通过改变数据类型来绕过拷贝引用这一过程,那么单纯的数组深拷贝是不是可以通过现有的几个API来实现呢?
var arr = [1,2,3]; var newArr = arr.toString().split(',').map(item => Number(item)) newArr[0] = 100; console.log(arr); // [1,2,3] console.log(newArr); // [100,2,3]
注意,此时仅能对包含纯数字的数组进行深拷贝,因为:
但我愿称它为纯数字数组深拷贝!
有的人会认为Object.assign()
,可以做到深拷贝,我们来看一下
var obj = {a: 1, b: { c: 2 } } var newObj = Object.assign({}, obj) newObj.a = 100; newObj.b.c = 200; console.log(obj); // {a: 1, b: { c: 200 } } console.log(newObj) // {a: 100, b: { c: 200 } }
神奇,第一层属性没有改变,但第二层却同步改变了,这是为什么呢?
因为 Object.assign()拷贝的是(可枚举)属性值。
假如源值是一个对象的引用,它仅仅会复制其引用值。MDN传送门
既然现有的方法无法实现深拷贝,不妨我们自己来实现一个吧~
我们只需要将所有属性即其嵌套属性原封不动的复制给新变量一份即可,抛开现有的方法,我们应该怎么做呢?
var shallowCopy = function(obj) { if (typeof obj !== 'object') return; // 根据obj的类型判断是新建一个数组还是对象 var newObj = obj instanceof Array ? [] : {}; // 遍历obj,并且判断是obj的属性才拷贝 for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = obj[key]; } } return newObj; }
我们只需要将所有属性的引用拷贝一份即可~
相信大家在实现深拷贝的时候都会想到递归,同样是判断属性值,但如果当前类型为object
则证明需要继续递归,直到最后
var deepCopy = function(obj) { if (typeof obj !== 'object') return; var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; } } return newObj; }
我们用白话来解释一下deepCopy
都做了什么
const obj = [1, { a: 1, b: { name: '余光'} } ]; const resObj = deepCopy(obj);
obj
,创建 第一个newObj
[]
0
(for in
以任意顺序遍历,我们假定按正常循序遍历)
1
obj[1]
另外请注意递归的方式虽然可以深拷贝,但是在性能上肯定不如浅拷贝,大家还是需要结合实际情况来选择。
作者: 余光
1. 随机排列
在开发者,有时候我们需要对数组的顺序进行重新的洗牌。 在 JS 中并没有提供数组随机排序的方法,这里提供一个随机排序的方法:
function shuffle(arr) {
var i, j, temp;
for (i = arr.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
return arr;
}
2. 唯一值
在开发者,我们经常需要过滤重复的值,这里提供几种方式来过滤数组的重复值。
使用 Set 对象
使用 Set() 函数,此函数可与单个值数组一起使用。对于数组中嵌套的对象值而言,不是一个好的选择。
const numArray = [1,2,3,4,2,3,4,5,1,1,2,3,3,4,5,6,7,8,2,4,6];
// 使用 Array.from 方法
Array.from(new Set(numArray));
// 使用展开方式
[...new Set(numArray)]
使用 Array.filter
使用 filter 方法,我们可以对元素是对象的进行过滤。
const data = [
{id: 1, name: 'Lemon'},
{id: 2, name: 'Mint'},
{id: 3, name: 'Mango'},
{id: 4, name: 'Apple'},
{id: 5, name: 'Lemon'},
{id: 6, name: 'Mint'},
{id: 7, name: 'Mango'},
{id: 8, name: 'Apple'},
]
function findUnique(data) {
return data.filter((value, index, array) => {
if (array.findIndex(item => item.name === value.name) === index) {
return value;
}
})
}
3. 使用 loadsh 的 lodash 方法
import {uniqBy} from 'lodash'
const data = [
{id: 1, name: 'Lemon'},
{id: 2, name: 'Mint'},
{id: 3, name: 'Mango'},
{id: 4, name: 'Apple'},
{id: 5, name: 'Lemon'},
{id: 6, name: 'Mint'},
{id: 7, name: 'Mango'},
{id: 8, name: 'Apple'},
]
function findUnique(data) {
return uniqBy(data, e => {
return e.name
})
}
3. 按属性对 对象数组 进行排序
我们知道 JS 数组中的 sort 方法是按字典顺序进行排序的,所以对于字符串类, 该方法是可以很好的正常工作,但对于数据元素是对象类型,就不太好使了,这里我们需要自定义一个排序方法。
在比较函数中,我们将根据以下条件返回值:
小于0:A 在 B 之前
大于0 :B 在 A 之前
等于0 :A 和 B 彼此保持不变
const data = [
{id: 1, name: 'Lemon', type: 'fruit'},
{id: 2, name: 'Mint', type: 'vegetable'},
{id: 3, name: 'Mango', type: 'grain'},
{id: 4, name: 'Apple', type: 'fruit'},
{id: 5, name: 'Lemon', type: 'vegetable'},
{id: 6, name: 'Mint', type: 'fruit'},
{id: 7, name: 'Mango', type: 'fruit'},
{id: 8, name: 'Apple', type: 'grain'},
]
function compare(a, b) {
// Use toLowerCase() to ignore character casing
const typeA = a.type.toLowerCase();
const typeB = b.type.toLowerCase();
let comparison = 0;
if (typeA > typeB) {
comparison = 1;
} else if (typeA < typeB) {
comparison = -1;
}
return comparison;
}
data.sort(compare)
4. 把数组转成以指定符号分隔的字符串
JS 中有个方法可以做到这一点,就是使用数组中的 .join() 方法,我们可以传入指定的符号来做数组进行分隔。
const data = ['Mango', 'Apple', 'Banana', 'Peach']
data.join(',');
// return "Mango,Apple,Banana,Peach"
5. 从数组中选择一个元素
对于此任务,我们有多种方式,一种是使用 forEach 组合 if-else 的方式 ,另一种可以使用filter 方法,但是使用forEach 和filter的缺点是:
在forEach中,我们要额外的遍历其它不需要元素,并且还要使用 if 语句来提取所需的值。
在filter 方法中,我们有一个简单的比较操作,但是它将返回的是一个数组,而是我们想要是根据给定条件从数组中获得单个对象。
为了解决这个问题,我们可以使用 find函数从数组中找到确切的元素并返回该对象,这里我们不需要使用if-else语句来检查元素是否满足条件。
const data = [
{id: 1, name: 'Lemon'},
{id: 2, name: 'Mint'},
{id: 3, name: 'Mango'},
{id: 4, name: 'Apple'}
]
const value = data.find(item => item.name === 'Apple')
// value = {id: 4, name: 'Apple'}
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
web中开发的三个基本技术(html5,css3,JavaScript)
html简介:html语言是纯文本类型的语言,是internet上用来编写网页的主要语言,使用HTML语言编写的网页文件也是标准的纯文本文件(简单说告诉浏览器显示什么)
.
css简介:css是一种网页控制技术,采用css技术,可以有效地对页面、字体、颜色、背景和其他效果实现更加精准的控制
(简单的说告诉浏览器如何显示)
.
JavaScript:JavaScript是web页面中的一种脚本编程语言,也是一种通用的、跨平台的、基于对象和事件驱动并具有安全性的脚本语言。它不需要进行编译,而是直接嵌入HTML页面中,把静态页面变成动态页面。(简单的来说告诉浏览器如何交互)
简单HTML文件结构
<html>/*文件开始*/ <head>/*文件头*/ <title>标题</title>/*文件标题*/ </head> <body>内容</body> </html>/*文件结束*/
HTML常用的标记
<br>换行 <p></p>段落 <s></s>删除线 <b></b>字体粗体 <u></u>下划线 <em></em>斜体内容 <sub></sub> 下标 <sup></sup>上标 <hr></hr>水平线 <a></a>超链接 .....
Elasticsearch(下面简称ES)中的bool查询在业务中使用也是比较多的。在一些非实时的分页查询,导出的场景,我们经常使用bool查询组合各种查询条件。
Bool查询包括四种子句,
must
filter
should
must_not
我这里只介绍下must和filter两种子句,因为是我们今天要讲的重点。其它的可以自行查询官方文档。
must, 返回的文档必须满足must子句的条件,并且参与计算分值
filter, 返回的文档必须满足filter子句的条件。但是跟Must不一样的是,不会计算分值, 并且可以使用缓存
从上面的描述来看,你应该已经知道,如果只看查询的结果,must和filter是一样的。区别是场景不一样。如果结果需要算分就使用must,否则可以考虑使用filter。
光说比较抽象,看个例子,下面两个语句,查询的结果是一样的。
使用filter过滤时间范围,
GET kibana_sample_data_ecommerce/_search { "size": 1000, "query": { "bool": { "must": [ {"term": { "currency": "EUR" }} ], "filter": { "range": { "order_date": { "gte": "2020-01-25T23:45:36.000+00:00", "lte": "2020-02-01T23:45:36.000+00:00" } } } } } }
filter比较的原理
上一节你已经知道了must和filter的基本用法和区别。简单来讲,如果你的业务场景不需要算分,使用filter可以真的让你的查询效率飞起来。
为了说明filter查询的原因,我们需要引入ES的一个概念 query context和 filter context。
query context
query context关注的是,文档到底有多匹配查询的条件,这个匹配的程度是由相关性分数决定的,分数越高自然就越匹配。所以这种查询除了关注文档是否满足查询条件,还需要额外的计算相关性分数.
filter context
filter context关注的是,文档是否匹配查询条件,结果只有两个,是和否。没有其它额外的计算。它常用的一个场景就是过滤时间范围。
并且filter context会自动被ES缓存结果,效率进一步提高。
对于bool查询,must使用的就是query context,而filter使用的就是filter context。
我们可以通过一个示例验证下。继续使用第一节的例子,我们通过kibana自带的search profiler来看看ES的查询的详细过程。
使用must查询的执行过程是这样的:
可以明显看到,此次查询计算了相关性分数,而且score的部分占据了查询时间的10分之一左右。
filter的查询我就不截图了,区别就是score这部分是0,也就是不计算相关性分数。
除了是否计算相关性算分的差别,经常使用的过滤器将被Elasticsearch自动缓存,以提高性能。
我自己曾经在一个项目中,对一个业务查询场景做了这种优化,当时线上的索引文档数量大概是3000万左右,改成filter之后,查询的速度几乎快了一倍。
我们应该根据自己的实际业务场景选择合适的查询语句,在某些不需要相关性算分的查询场景,尽量使用filter context
可以让你的查询更加。
为了降低开发的复杂度,以后端为出发点,比如:Struts、SpringMVC 等框架的使用,就是后端的 MVC 时代;
以 SpringMVC 流程为例:
优点:
前后端职责很清晰: 前端工作在浏览器端,后端工作在服务端。清晰的分工,可以让开发并行,测 试数据的模拟不难,前端可以本地开发。后端则可以专注于业务逻辑的处理,输出 RESTful等接 口。
前端开发的复杂度可控: 前端代码很重,但合理的分层,让前端代码能各司其职。这一块蛮有意思 的,简单如模板特性的选择,就有很多很多讲究。并非越强大越好,限制什么,留下哪些自由,代 码应该如何组织,所有这一切设计,得花一本书的厚度去说明。
-部署相对独立: 可以快速改进产品体验
缺点:
代码不能复用。比如后端依旧需要对数据做各种校验,校验逻辑无法复用浏览器端的代码。如果可 以复用,那么后端的数据校验可以相对简单化。
全异步,对 SEO 不利。往往还需要服务端做同步渲染的降级方案。 性能并非最佳,特别是移动互联网环境下。
SPA 不能满足所有需求,依旧存在大量多页面应用。URL Design 需要后端配合,前端无法完全掌控。
NodeJS 带来的全栈时代
前端为主的 MV* 模式解决了很多很多问题,但如上所述,依旧存在不少不足之处。随着 NodeJS 的兴 起,JavaScript 开始有能力运行在服务端。这意味着可以有一种新的研发模式:
————————————————
版权声明:本文为CSDN博主「叁有三分之一」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/iME_cho/article/details/105654633
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值
如果解构不成功,变量的值就等于undefined
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错、
交换变量的值
例如:let x=1,y=2;[x,y] = [y,x]
从函数返回多个值
函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便
函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来
提取 JSON 数据,很多接口数据只需要其中某部分
例如aa.axios.get(res=>{let {data:result}=res;}),则res.data.result = result了
函数参数的默认值
指定参数的默认值,就避免了在函数体内部再写var foo = config.foo || ‘default foo’;这样的语句
遍历 Map 结构
Map 结构原生支持 Iterator 接口,配合变量的解构赋值,获取键名和键值就非常方便
输入模块的指定方法
加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。* const { SourceMapConsumer, SourceNode } = require(“source-map”);
左右两侧数据解构须得吻合,或者等号左边的模式,只匹配一部分的等号右边的数组(属于不完全解构)
特殊情况使用…扩展运算符,无值是空数组
左右两边等式的性质要相同,等号的右边不是数组(或者严格地说,不是可遍历的结构),那么将会报错,只要某种数据结构具有 Iterator
接口,都可以采用数组形式的解构赋值,例如Set结构
解构赋值允许指定默认值,当一个数组成员严格等于undefined,默认值才会生效,否则取赋值的值;如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值;默认值可以引用解构赋值的其他变量,但该变量必须已经声明
// 数组的解构赋值 let [a,b] = [1,2]; console.log([a,b],a);//[1, 2] 1 let [aa] = [11,22]; console.log(aa)//11 let [aaa,bbb] = [111]; console.log(aaa,bbb)//111 undefined let [head, ...tail] = [1, 2, 3, 4]; console.log(head,tail)//1,[2,3,4] let [x, y, ...z] = ['a']; console.log(x,y,z)//a undefined [] // 等号右边不是数组会报错 // let [ab] = 121; // conosle.log(ab)//TypeError: 121 is not iterable // let [abc] = {} // conosle.log(abc)//TypeError: {} is not iterable // 默认值赋值 let [zz = 1] = [undefined]; console.log(zz)//1 let [zzz = 1] = [null]; console.log(zzz)//null let [foo = true] = []; console.log(foo)// true let [xxx, yyy = 'b'] = ['a']; console.log(xxx,yyy)//a,b let [xxxx, yyyy = 'b'] = ['a', undefined]; console.log(xxxx,yyyy)//a,b function f() { console.log('aaa'); } let [xx = f()] = [1]; console.log(xx)//1 let [qq=ww,ww=11] = [23,44]; console.log(qq,ww)//23,44,因为ww申明比qq晚所以是undefined;
2、对象的解构赋值
对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者
数组是按照位置区分,对象则是按照键名区分的,同样的解构失败则为undefine
可将已有方法对象解构赋值
嵌套赋值,注意是变量是否被赋值是模式还是键值
对象的解构赋值可以取到继承的属性
如果要将一个已经声明的变量用于解构赋值,必须非常小心
let xx; // {xx} = {xx: 1}这样会报错,
解构赋值允许等号左边的模式之中,不放置任何变量名。因此,可以写出非常古怪的赋值表达式
({} = [true, false]);//可执行
由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构
objFuc(){ // 对象解构赋值 let {b,a} = {a:1} console.log(a,b)//1 undefined // 已有对象解构赋值 let { sin, cos } = Math;//将Math对象的对数、正弦、余弦三个方法,赋值到对应的变量上 console.log(sin);//log() { [native code] } const { log } = console; log('hello') // hello // let { foo: baz } = { foo: 'aaa', bar: 'bbb' }; console.log(baz);//aaa // 嵌套赋值 let obj = { p: [ 'Hello', { y: 'World' } ] }; let { p,p:[x, { y }] } = obj; console.log(x,y,p)//Hello World p: ['Hello',{ y: 'World' }] //继承赋值 const obj1 = {}; const obj2 = { foo: 'bar' }; Object.setPrototypeOf(obj1, obj2);//obj1继承obj2 const { foo } = obj1; console.log(foo) // "bar" // 默认值 // 错误的写法 let xx; // {xx} = {xx: 1};// SyntaxError: syntax error,Uncaught SyntaxError: Unexpected token '=' ({xx} = {xx: 1});//正确写法 console.log(xx) // 古怪的,等式左边可为空 // ({} = [true, false]); // 对象可解构数组 let arr = [1, 2, 3]; let {0 : first, [arr.length - 1] : last} = arr; console.log(first,last)//1 3 },
strFuc(){ // str:'yan_yan' let [a,b,c,d,e,f,g] = this.str; console.log(a,b,c,d,e,f,g)//y a n _ y a n // 对数组属性解构赋值 let {length} = this.str; console.log(length)//7 },
4、数值和布尔值的解构赋值
let {toString: s} = 123; console.log(s === Number.prototype.toString,s)//true ƒ toString() { [native code] } let {toString: ss} = true; console.log(ss === Boolean.prototype.toString,ss)// true ƒ toString() { [native code] } // 右侧必须是数组或对象,undefined和null无法转为对象,所以对它们进行解构赋值,都会报错 // let { prop: x } = undefined; // TypeError // let { prop: y } = null; // TypeError
5、函数参数的解构赋值
// 函数的解构赋值可使用默认值,注意默认值是指实参的默认值而不是形参的默认值 function move({x=1, y=1}={}) { return [x, y]; } function move1({x, y} = { x: 0, y: 0 }) { return [x, y]; } function move2({x, y=1} = { x: 0, y: 0 }) { return [x, y]; } console.log(move({x: 3, y: 8})); // [3, 8] console.log(move({x: 3})); // [3, 1] console.log(move({})); // [1, 1] console.log(move()); // [1,1] console.log(move1({x: 3, y: 8})); // [3, 8] console.log(move1({x: 3})); // [3, 1] console.log(move1({})); // [undefined, 1] console.log(move1()); // [0,0] console.log(move2({x: 3, y: 8})); // [3, 8] console.log(move2({x: 3})); // [3, 1] console.log(move2({})); // [undefined, 1] console.log(move2()); // [0,0]
6、圆括号问题 解构赋值虽然很方便,但是解析起来并不容易。对于编译器来说,一个式子到底是模式,还是表达式,没有办法从一开始就知道,必须解析到(或解析不到)等号才能知道。 由此带来的问题是,如果模式中出现圆括号怎么处理。ES6 的规则是,只要有可能导致解构的歧义,就不得使用圆括号。 可以使用圆括号的情况只有一种:赋值语句的非模式部分,可以使用圆括号 总结: 不管是哪一类的解构赋值,等式右边的数据必须是对象形式(数组也是一种对象形式) ———————————————— 版权声明:本文为CSDN博主「Yan_an_n」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_44258964/article/details/105643553
在了解了javascript的语言基础和特性后
javascript真正大放光彩的地方来了——这就是javascript DOM
Javascript DOM
DOM(Document Object Model),文档对象模型。
是W3C组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口;W3C已经定义了一系列DOM接口,通过这些DOM接口可以改变网页的内容、结构和样式。
简单的说就是一套操作文档内容的方法。
需要注意的是,我们需要把DOM当作一个整体,不能分割看待,即DOM(文档对象模型)是一套操作文档内容的方法。
DOM把以上内容看作都是对象
<!DOCTYPE html> <html> <head> <title>Shopping list</title> <meta charset="utf-8"> </head> <body> <h1>What to buy</h1> <p id="buy" title="a gentle reminder">Don't forget to buy this stuff</p> <ul id="purchases"> <li>A tin od beans</li> <li>Cheese</li> <li>Milk</li> </ul> </body> </html>
1、获取DOM四种基本方法
1、getElementById()
2、getElementsByTagname()
3、getAttribute()
4、setAttribute()
常用的两种解析:
1. getElementById():
参数:元素的ID值。 (元素节点简称元素)
返回值:一个有指定ID的元素对象(元素是对象)
注:这个方法是与document对象相关联,只能由document对象调用。
用法:document.getElementById(Id)
例:
<!DOCTYPE html> <html lang="zh"> <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>Document</title> </head> <body> <div id="time">2020-04-16</div> <script> // 1. 因为我们文档页面从上往下加载,所以先得有标签 所以我们script写到标签的下面 // 2. get 获得 element 元素 by 通过 驼峰命名法 // 3. 参数 id是大小写敏感的字符串 // 4. 返回的是一个元素对象 var timer = document.getElementById('time'); console.log(timer); console.log(typeof timer); // 5. console.dir 打印我们返回的元素对象 更好的查看里面的属性和方法 console.dir(timer); </script> </body> </html>
看一下控制台打印的是什么
可以看到 console.log(timer)打印出来的是整个div标签
timer类型是个对象
2. getElementsByTagName():
参数:元素名
返回值:一个对象数组。这个数组里每个元素都是对象,每个对象分别对应着文档里给定标签的一个元素。
注:这个方法可和一般元素关联。这个方法允许我们把通配符当作它的参数,返回在某份html文档里总共有多少个元素节点。
用法:element.getElementsByTagName(TagName)
例:
var items=document.getElementsByTagName("li");
items.length;//3
document.getElementsByTagName(“*”);//12
2、事件基础
3.1 事件概述
JavaScript使我们有能力创建动态页面,而事件是可以被JavaScript侦测到的行为。
简单理解:触发——>响应机制
网页中每个元素都可以产生某些可以触发JavaScript的事件,例如,我们可以在用户点击某按钮产生一个事件,然后去执行某些操作
3.2 事件三要素
事件源 、事件类型、事件处理程序,我们也称为事件三要素
(1) 事件源 事件被触发的对象 谁
(2) 事件类型 如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下
(3) 事件处理程序 通过一个函数赋值的方式 完成
代码实例
<!DOCTYPE html> <html lang="zh"> <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>Document</title> </head> <body> <button id="btn">唐伯虎</button> <script> // 点击一个按钮,弹出对话框 // 1. 事件是有三部分组成 事件源 事件类型 事件处理程序 我们也称为事件三要素 //(1) 事件源 事件被触发的对象 谁 按钮 var btn = document.getElementById('btn'); //(2) 事件类型 如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下 //(3) 事件处理程序 通过一个函数赋值的方式 完成 btn.onclick = function() { alert('点秋香'); } </script> </body> </html>
运行结果
1、获取事件源
2、注册事件(绑定事件)
3、添加事件处理程序(采取函数赋值形式)
代码实战
-
<!DOCTYPE html> <html lang="zh"> <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>Document</title> </head> <body> <div>123</div> <script> // 执行事件步骤 // 点击div 控制台输出 我被选中了 // 1. 获取事件源 var div = document.querySelector('div'); // 2.绑定事件 注册事件 // div.onclick // 3.添加事件处理程序 div.onclick = function() { console.log('我被选中了'); } </script> </body> </html>
常用的DOM事件
onclick事件---当用户点击时执行
onload事件---当用户进入时执行
onunload事件---用用户离开时执行
onmouseover事件---当用户鼠标指针移入时执行
onmouseout事件---当用户鼠标指针移出时执行
onmousedown事件---当用户鼠标摁下时执行
onmouseup事件---当用户鼠标松开时执行
————————————————
版权声明:本文为CSDN博主「那是我呐」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42402867/article/details/105567787
自己写了段jquery的ajax请求,并输出到页面的表格中
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>jQuery实现JSONP</title> </head> <body> <div id="mydiv"> <button id="btn">点击</button> <div id="container"> <!--每个条目--> <div class="item"> <div class="item-title">作业名字</div> <div> <span class="item-content">创建时间</span> <span class="item-content">发布老师</span> </div> </div> </div> </div> </body> <script type="text/javascript" src="https://code.jquery.com/jquery-3.1.0.min.js"></script> <script type="text/javascript"> $(function(){ $("#btn").click(function(){ $.ajax({ async : true, url : "xxxxxxxxxxx", type : "GET", dataType : "json", // 返回的数据类型,设置为JSONP方式 jsonp : 'callback', //指定一个查询参数名称来覆盖默认的 jsonp 回调参数名 callback jsonpCallback: 'handleResponse', //设置回调函数名 data : { q : "javascript", count : 1, city:'北京' }, success: function(response, status, xhr){ // console.log('状态为:' + status + ',状态是:' + xhr.statusText); // console.log(response); var group = response.hourly_forecast; console.log(group,111111111) console.log(group.length,222222222222) //拼接字符串 var str = ''; //对数据做遍历,拼接到页面显示 for(var i=0;i<group.length;i++){ str += '<div class="item">'+ '<div class="item-title">'+ group[i].date +'</div>'+ '<div>'+ '<span class="item-content">'+ group[i].hum +'</span>'+ '<span class="item-content">'+ group[i].pop +'</span>'+ '</div>'+ '</div>'; } //放入页面的容器显示 $('#container').html(str); } }); }); }); function handleResponse(response){ // 对response数据进行操作代码 alert("jsonp success!"); } </script> </html>
一、处理跨域的方式:
1.代理
2.XHR2
HTML5中提供的XMLHTTPREQUEST Level2(及XHR2)已经实现了跨域访问。但ie10以下不支持
只需要在服务端填上响应头:
header("Access-Control-Allow-Origin:*"); /*星号表示所有的域都可以接受,*/ header("Access-Control-Allow-Methods:GET,POST");
3.jsonP
原理:
ajax本身是不可以跨域的, 通过产生一个script标签来实现跨域。因为script标签的src属性是没有跨域的限制的。
其实设置了dataType: 'jsonp'后,$.ajax方法就和ajax XmlHttpRequest没什么关系了,取而代之的则是JSONP协议。JSONP是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问。
ajax的跨域写法:
(其余写法和不跨域的一样):
比如
做过前端开发的小伙伴就算不是非常理解重排与重绘,但是肯定都听过这两个词。那为什么这两个东西这么重要?因为他与我们的页面性能息息相关,今天,我们就来好好研究一下这两个东西。
浏览器的渲染流程
在讲解重排和重绘之前,我们有必要说一下浏览器的渲染流程。下面是浏览器渲染过程中最关键的几个部分。如果想了解完整的浏览器渲染流程,推荐大家去阅读李兵老师的浏览器工作原理实践,需要付费阅读。后期我也会整理一下再出一篇博客详细介绍浏览器的渲染过程。
JavaScript:一般来说,我们会使用 JavaScript 来实现一些视觉变化的效果。比如用 jQuery 的 animate 函数做一个动画、对一个数据集进行排序或者往页面里添加一些 DOM 元素等。当然,除了 JavaScript,还有其他一些常用方法也可以实现视觉变化效果,比如:CSS Animations、Transitions 和 Web Animation API。
样式计算:此过程是根据匹配选择器(例如 .headline 或 .nav > .nav__item)计算出哪些元素应用哪些 CSS 规则的过程。从中知道规则之后,将应用规则并计算每个元素的最终样式。
布局:在知道对一个元素应用哪些规则之后,浏览器即可开始计算它要占据的空间大小及其在屏幕的位置。网页的布局模式意味着一个元素可能影响其他元素,例如 元素的宽度一般会影响其子元素的宽度以及树中各处的节点,因此对于浏览器来说,布局过程是经常发生的。
绘制:绘制是填充像素的过程。它涉及绘出文本、颜色、图像、边框和阴影,基本上包括元素的每个可视部分。绘制一般是在多个表面(通常称为层)上完成的。
合成:由于页面的各部分可能被绘制到多层,由此它们需要按正确顺序绘制到屏幕上,以便正确渲染页面。对于与另一元素重叠的元素来说,这点特别重要,因为一个错误可能使一个元素错误地出现在另一个元素的上层。
其中,重排和重绘影响的就是其中的布局和绘制过程。
什么是重排和重绘制
重排:当DOM的变化引发了元素几何属性的变化,比如改变元素的宽高,元素的位置,导致浏览器不得不重新计算元素的几何属性,并重新构建渲染树,这个过程称为“重排”。
重绘:完成重排后,要将重新构建的渲染树渲染到屏幕上,这个过程就是“重绘”。
简单来说,涉及元素的几何更新时,叫重排。而只涉及样式更新而不涉及几何更新时,叫重绘。对于两者来说,重排必定引起重绘,但是重绘并不一定引起重排。所以,当涉及重排时,浏览器会将上述的步骤再次执行一遍。当只涉及重绘时,浏览器会跳过Layout步骤,即:
而如果既不需要重排,也不需要重绘,那么就是下面这样:
浏览器会直接跳到合成阶段。显然,对于页面性能来说,不重排也不重绘 > 重绘 > 重排。
什么操作会引起重排和重绘
显然,触发重排的一般都是几何因素,这是比较好理解的:
页面第一次渲染 在页面发生首次渲染的时候,所有组件都要进行首次布局,这是开销最大的一次重排
浏览器窗口尺寸改变
元素位置和尺寸发生改变的时候
新增和删除可见元素
内容发生改变(文字数量或图片大小等等)
元素字体大小变化
还有其他一些操作也可能引发重排
查询某些属性或调用某些方法
offset(Top|Left|Width|Height)
scroll(Top|Left|Width|Height)
client(Top|Left|Width|Height)
getComputedStyle()
我们可能不太理解为什么这些操作也能引起重排,这里我先简单解释一下。因为现在的浏览器已经非常完善了,会自动帮我们做一些优化。当我们用js操作DOM的时候,浏览器并不是立马执行的,而是将操作存储在一个队列中。当达到一定数量或者经过一定时间以后浏览器再统一的去执行队列中的操作。那么回到我们刚才的问题,为什么查询这些属性也会导致重排?因为当你查询这些属性时,浏览器就会强制刷新队列,因为如果不立马执行队列中的操作,有可能得到的结果就是错误的。所以相当于你强制打断了浏览器的优化流程,引发了重排。下面我们通过一些小例子来进一步理解这段话:
首先我们来一个显然会引发重排的操作
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> #test { width: 100px; height: 100px; background-color: red; position: relative; } </style> </head> <body> <div id="test"> </div> <button onclick="reflow()">click</button> <script> function reflow() { var div = document.querySelector("#test"); div.style.left = '200px'; } </script> </body> </html>
把时间轴往后拉,可以看到这几个过程,先简单介绍一些这些名词代表的含义:
Recalculate Style:这个过程就是生成CSSOM的过程
Layout:这就是布局阶段,即重排的过程
Update Layer Tree:这个阶段是更新层树的过程
Paint:该阶段是为每一层准备绘制列表的过程
Composite Layers:该阶段是利用绘制列表来生成相应图层的位图了,还涉及到合成线程和光栅化,performence面板中的Raster就是光栅化线程池 。
这里只做一个简单的介绍,对其中内容不太明白的同学可以参考李兵老师的文章或者在我的下一篇介绍浏览器渲染过程的文章中会详细解释。
那通过这个图我们可以看到,我们改变了div的left之后就触发了Layout,即重排的过程。下面我们仅改变div的背景颜色,给大家一个对比。
即不重排也不重绘
说完了重排和重绘,不要忘记我们最开始提到的,最的方式就是跳过重排和重绘阶段。你可能会想,什么情况下可以做到这一点?其实这就是我们平时说的GPU加速,具体是如何实现呢?在开发过程中,如果我们使用了某些属性,浏览器会帮助我们将使用了该属性的div提升到一个单独的合成层,而在后面的渲染中,提升到该层的div将跳过重排和重绘的操作,直接到合成阶段。在stack overflow上有问题提到了这块内容。我们翻译一下就是:
下面几个属性能让浏览器帮助我们将div提升到一个单独的合成层:
图层具有3D或透视变换CSS属性
使用加速视频解码的 video 元素
拥有 3D(WebGL) 上下文或者加速 2D 上下文的 canvas 元素
混合插件(Flash)
对自己的 opacity 做 CSS 动画或使用一个动画 webkit 变换的元素
图层使用加速的CSS过滤器
层具有作为合成层的后代
图层具有较低z索引的同级元素,该同级元素具有合成层(换句话说,该层在合成层的顶部渲染)
css will-change属性
最后一点是我加上去的,同时根据文中的内容我们可以知道,css3硬件加速是浏览器的行为,所以在不同浏览器下可能会有不同的表现形式。下面我们用一个例子来理解一下。这是李兵老师在他的专栏中提出的一个例子,我拿过来借用一下,注意box中的will-change属性:
<html> <head> <title>观察will-change</title> <style> .box { will-change: transform, opacity; display: block; float: left; width: 40px; height: 40px; margin: 15px; padding: 10px; border: 1px solid rgb(136, 136, 136); background: rgb(187, 177, 37); border-radius: 30px; transition: border-radius 1s ease-out; } body { font-family: Arial; } </style> </head> <body> <div id="controls"> <button id="start">start</button> <button id="stop">stop</button> </div> <div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> <div class="box">旋转盒子</div> </div> <script> let boxes = document.querySelectorAll('.box'); let boxes1 = document.querySelectorAll('.box1'); let start = document.getElementById('start'); let stop = document.getElementById('stop'); let stop_flag = false start.addEventListener('click', function () { stop_flag = false requestAnimationFrame(render); }) stop.addEventListener('click', function () { stop_flag = true }) let rotate_ = 0 let opacity_ = 0 function render() { if (stop_flag) return 0 rotate_ = rotate_ + 6 if (opacity_ > 1) opacity_ = 0 opacity_ = opacity_ + 0.01 let command = 'rotate(' + rotate_ + 'deg)'; for (let index = 0; index < boxes.length; index++) { boxes[index].style.transform = command boxes[index].style.opacity = opacity_ } requestAnimationFrame(render); } </script> </body> </html>
————————————————
版权声明:本文为CSDN博主「溪宁」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_38164763/article/details/105406580
蓝蓝设计的小编 http://www.lanlanwork.com