JavaScript是一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标准通用标记语言下的一个应用)网页上使用,用来给HTML网页增加动态功能。
js语言是弱语言类型, 因此我们在项目开发中当我们随意更该某个变量的数据类型后
有可能会导致其他引用这个变量的方法中报错等等。
CSSSprites(精灵图),将一个页面涉及到的所有图片都包含到一张大图中去,然后利用CSS的 background-image,background-repeat,background-position属性的组合进行背景定位。
优点:
CSS Sprites
能很好地减少网页的http请求,从而大大提高了页面的性能,这是CSS Sprites
最大的优点;CSS Sprites
能减少图片的字节,把3张图片合并成1张图片的字节总是小于这3张图片的字节总和。缺点:
CSSSprites
在开发的时候相对来说有点麻烦,需要借助photoshop
或其他工具来对每个背景单元测量其准确的位置。CSS Sprites
在维护的时候比较麻烦,页面背景有少许改动时,就要改这张合并的图片,无需改的地方尽量不要动,这样避免改动更多的CSS
,如果在原来的地方放不下,又只能(最好)往下加图片,这样图片的字节就增加了,还要改动CSS
。typeof null 的结果是Object。
在 JavaScript 第一个版本中,所有值都存储在 32 位的单元中,每个单元包含一个小的 类型标签(1-3 bits) 以及当前要存储值的真实数据。类型标签存储在每个单元的低位中,共有五种数据类型:
000: object - 当前存储的数据指向一个对象。
1: int - 当前存储的数据是一个 31 位的有符号整数。
010: double - 当前存储的数据指向一个双精度的浮点数。
100: string - 当前存储的数据指向一个字符串。
110: boolean - 当前存储的数据是布尔值。
如果最低位是 1,则类型标签标志位的长度只有一位;如果最低位是 0,则类型标签标志位的长度占三位,为存储其他四种数据类型提供了额外两个 bit 的长度。
有两种特殊数据类型:
那也就是说null的类型标签也是000,和Object的类型标签一样,所以会被判定为Object。
await 在等待什么呢? 一般来说,都认为 await 是在等待一个 async 函数完成。不过按语法说明,await 等待的是一个表达式,这个表达式的计算结果是 Promise 对象或者其它值(换句话说,就是没有特殊限定)。
因为 async 函数返回一个 Promise 对象,所以 await 可以用于等待一个 async 函数的返回值——这也可以说是 await 在等 async 函数,但要清楚,它等的实际是一个返回值。注意到 await 不仅仅用于等 Promise 对象,它可以等任意表达式的结果,所以,await 后面实际是可以接普通函数调用或者直接量的。所以下面这个示例完全可以正确运行:
function getSomething() {
return "something";
}
async function testAsync() {
return Promise.resolve("hello async");
}
async function test() {
const v1 = await getSomething();
const v2 = await testAsync();
console.log(v1, v2);
}
test();
await 表达式的运算结果取决于它等的是什么。
来看一个例子:
function testAsy(x){
return new Promise(resolve=>{setTimeout(() => {
resolve(x);
}, 3000)
}
)
}
async function testAwt(){
let result = await testAsy('hello world');
console.log(result); // 3秒钟之后出现hello world
console.log('cuger') // 3秒钟之后出现cug
}
testAwt();
console.log('cug') //立即输出cug
这就是 await 必须用在 async 函数中的原因。async 函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。await暂停当前async的执行,所以'cug''最先输出,hello world'和‘cuger’是3秒钟后同时出现的。
在 Vue3.0 中通过 Proxy
来替换原本的 Object.defineProperty
来实现数据响应式。
Proxy 是 ES6 中新增的功能,它可以用来自定义对象中的操作。
let p = new Proxy(target, handler)
target
代表需要添加代理的对象,handler
用来自定义对象中的操作,比如可以用来自定义 set
或者 get
函数。
下面来通过 Proxy
来实现一个数据响应式:
let onWatch = (obj, setBind, getLogger) => {
let handler = {
get(target, property, receiver) {
getLogger(target, property)
return Reflect.get(target, property, receiver)
},
set(target, property, value, receiver) {
setBind(value, property)
return Reflect.set(target, property, value)
}
}
return new Proxy(obj, handler)
}
let obj = { a: 1 }
let p = onWatch(
obj,
(v, property) => {
console.log(`监听到属性${property}改变为${v}`)
},
(target, property) => {
console.log(`'${property}' = ${target[property]}`)
}
)
p.a = 2 // 监听到属性a改变
p.a // 'a' = 2
在上述代码中,通过自定义 set
和 get
函数的方式,在原本的逻辑中插入了我们的函数逻辑,实现了在对对象任何属性进行读写时发出通知。
当然这是简单版的响应式实现,如果需要实现一个 Vue 中的响应式,需要在 get
中收集依赖,在 set
派发更新,之所以 Vue3.0 要使用 Proxy
替换原本的 API 原因在于 Proxy
无需一层层递归为每个属性添加代理,一次即可完成以上操作,性能上更好,并且原本的实现有一些数据更新不能监听到,但是 Proxy
可以完美监听到任何方式的数据改变,唯一缺陷就是浏览器的兼容性不好。
在 JavaScript 中,基本类型是没有属性和方法的,但是为了便于操作基本类型的值,在调用基本类型的属性或方法时 JavaScript 会在后台隐式地将基本类型的值转换为对象,如:
const a = "abc";
a.length; // 3
a.toUpperCase(); // "ABC"
在访问'abc'.length
时,JavaScript 将'abc'
在后台转换成String('abc')
,然后再访问其length
属性。
JavaScript也可以使用Object
函数显式地将基本类型转换为包装类型:
var a = 'abc'
Object(a) // String {"abc"}
也可以使用valueOf
方法将包装类型倒转成基本类型:
var a = 'abc'
var b = Object(a)
var c = b.valueOf() // 'abc'
看看如下代码会打印出什么:
var a = new Boolean( false );
if (!a) {
console.log( "Oops" ); // never runs
}
答案是什么都不会打印,因为虽然包裹的基本类型是false
,但是false
被包裹成包装类型后就成了对象,所以其非值为false
,所以循环体中的内容不会运行。
(1)箭头函数比普通函数更加简洁
如果没有参数,就直接写一个空括号即可
如果只有一个参数,可以省去参数括号
如果有多个参数,用逗号分割
如果函数体的返回值只有一句,可以省略大括号
如果函数体不需要返回值,且只有一句话,可以给这个语句前面加一个void关键字。最常用的就是调用一个函数:
let fn = () => void doesNotReturn()
(2) 箭头函数没有自己的this
箭头函数不会创建自己的this,所以它没有自己的this,它只会在自己作用域的上一层继承this。所以箭头函数中的this的指向在它在定义时一家确定了,之后不会改变。
(3)箭头函数继承来的this指向永远不会改变
(4) call()、apply()、bind()等方法不能改变箭头函数中的this指向
(5) 箭头函数不能作为构造函数使用
(6) 箭头函数没有自己的arguments
(7) 箭头函数没有prototype
(8) 箭头函数不能用作Generator函数,不能使用yeild关键字
.parent { position: relative;} .child { position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%);}
.parent {
position: relative;
}
.child {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
.parent {
position: relative;
}
.child {
position: absolute;
top: 50%;
left: 50%;
margin-top: -50px; /* 自身 height 的一半 */
margin-left: -50px; /* 自身 width 的一半 */
}
.parent {
display: flex;
justify-content:center;
align-items:center;
}
JavaScript共有八种数据类型,分别是 Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。
其中 Symbol 和 BigInt 是ES6 中新增的数据类型:
这些数据可以分为原始数据类型和引用数据类型:
两种类型的区别在于存储位置的不同:
堆和栈的概念存在于数据结构和操作系统内存中,在数据结构中:
在操作系统中,内存被分为栈区和堆区:
NaN 指“不是一个数字”(not a number),NaN 是一个“警戒值”(sentinel value,有特殊用途的常规值),用于指出数字类型中的错误情况,即“执行数学运算没有成功,这是失败后返回的结果”。
typeof NaN; // "number"
NaN 是一个特殊值,它和自身不相等,是唯一一个非自反(自反,reflexive,即 x === x 不成立)的值。而 NaN !== NaN 为 true。
//利用绝对定位,先将元素的左上角通过 top:50%和 left:50%定位到页面的中心,然后再通过 translate 来调整元素的中心点到页面的中心。该方法需要考虑浏览器兼容问题。
.parent {
position: relative;
}
.child {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
//利用绝对定位,设置四个方向的值都为 0,并将 margin 设置为 auto,由于宽高固定,因此对应方向实现平分,可以实现水平和垂直方向上的居中。该方法适用于盒子有宽高的情况:
.parent {
position: relative;
}
.child {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
//利用绝对定位,先将元素的左上角通过 top:50%和 left:50%定位到页面的中心,然后再通过 margin 负值来调整元素的中心点到页面的中心。该方法适用于盒子宽高已知的情况
.parent {
position: relative;
}
.child {
position: absolute;
top: 50%;
left: 50%;
margin-top: -50px; /* 自身 height 的一半 */
margin-left: -50px; /* 自身 width 的一半 */
}
//使用 flex 布局,通过 align-items:center 和 justify-content:center 设置容器的垂直和水平方向上为居中对齐,然后它的子元素也可以实现垂直和水平的居中。该方法要**考虑兼容的问题**,该方法在移动端用的较多:
.parent {
display: flex;
justify-content:center;
align-items:center;
}
//另外,如果父元素设置了flex布局,只需要给子元素加上`margin:auto;`就可以实现垂直居中布局
.parent{
display:flex;
}
.child{
margin: auto;
}
vue-router是vuex.js官方的路由管理器,它和vue.js的核心深度集成,让构建但页面应用变得易如反掌
<router-link> 组件支持用户在具有路由功能的应用中 (点击) 导航。 通过 to 属性指定目标地址
<router-view> 组件是一个 functional 组件,渲染路径匹配到的视图组件。
<keep-alive> 组件是一个用来缓存组件
router.beforeEach
router.afterEach
to: Route: 即将要进入的目标 路由对象
from: Route: 当前导航正要离开的路由
next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
介绍了路由守卫及用法,在项目中路由守卫起到的作用等等
语法:
arr.forEach(callback(currentValue [, index [, array]])[, thisArg])
参数:callback
:为数组中每个元素执行的函数,该函数接受1-3个参数currentValue
: 数组中正在处理的当前元素index
(可选): 数组中正在处理的当前元素的索引array
(可选):forEach()
方法正在操作的数组thisArg
(可选): 当执行回调函数callback
时,用作this
的值。返回值:undefined
Array.prototype.forEach1 = function(callback, thisArg) {
if(this == null) {
throw new TypeError('this is null or not defined');
}
if(typeof callback !== "function") {
throw new TypeError(callback + 'is not a function');
}
// 创建一个新的 Object 对象。该对象将会包裹(wrapper)传入的参数 this(当前数组)。
const O = Object(this);
// O.length >>> 0 无符号右移 0 位
// 意义:为了保证转换后的值为正整数。
// 其实底层做了 2 层转换,第一是非 number 转成 number 类型,第二是将 number 转成 Uint32 类型
const len = O.length >>> 0;
let k = 0;
while(k < len) {
if(k in O) {
callback.call(thisArg, O[k], k, O);
}
k++;
}
}
语法:
arr.map(callback(currentValue [, index [, array]])[, thisArg])
参数:与forEach()
方法一样返回值:一个由原数组每个元素执行回调函数的结果组成的新数组。
Array.prototype.map1 = function(callback, thisArg) {
if(this == null) {
throw new TypeError('this is null or not defined');
}
if(typeof callback !== "function") {
throw new TypeError(callback + 'is not a function');
}
const O = Object(this);
const len = O.length >>> 0;
let newArr = []; // 返回的新数组
let k = 0;
while(k < len) {
if(k in O) {
newArr[k] = callback.call(thisArg, O[k], k, O);
}
k++;
}
return newArr;
}
语法:
arr.filter(callback(element [, index [, array]])[, thisArg])
参数:callback
: 用来测试数组的每个元素的函数。返回true
表示该元素通过测试,保留该元素,false
则不保留。它接受以下三个参数:element、index、array
,参数的意义与forEach
一样。thisArg
(可选): 执行callback
时,用于this
的值。返回值:一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数组。
Array.prototype.filter1 = function(callback, thisArg) {
if(this == null) {
throw new TypeError('this is null or not defined');
}
if(typeof callback !== "function") {
throw new TypeError(callback + 'is not a function');
}
const O = Object(this);
const len = O.length >>> 0;
let newArr = []; // 返回的新数组
let k = 0;
while(k < len) {
if(k in O) {
if(callback.call(thisArg, O[k], k, O)) {
newArr.push(O[k]);
}
}
k++;
}
return newArr;
}
语法:
arr.some(callback(element [, index [, array]])[, thisArg])
参数:callback
: 用来测试数组的每个元素的函数。接受以下三个参数:element、index、array,参数的意义与 forEach 一样。thisArg
(可选): 执行callback
时,用于this
的值。 返回值:数组中有至少一个元素通过回调函数的测试就会返回 true;所有元素都没有通过回调函数的测试返回值才会为 false。
Array.prototype.some1 = function(callback, thisArg) {
if(this == null) {
throw new TypeError('this is null or not defined');
}
if(typeof callback !== "function") {
throw new TypeError(callback + 'is not a function');
}
const O = Object(this);
const len = O.length >>> 0;
let k = 0;
while(k < len) {
if(k in O) {
if(callback.call(thisArg, O[k], k, O)) {
return true
}
}
k++;
}
return false;
}
语法:
arr.reduce(callback(preVal, curVal[, curIndex [, array]])[, initialValue])
参数:callback
: 一个 “reducer” 函数,包含四个参数:preVal
:上一次调用callback
时的返回值。在第一次调用时,若指定了初始值initialValue
,其值则为initialValue
,否则为数组索引为 0 的元素array[0]
。curVal
:数组中正在处理的元素。在第一次调用时,若指定了初始值initialValue
,其值则为数组索引为 0 的元素array[0]
,否则为array[1]
。curIndex
(可选):数组中正在处理的元素的索引。若指定了初始值initialValue
,则起始索引号为 0,否则从索引 1 起始。array
(可选):用于遍历的数组。 initialValue(可选): 作为第一次调用callback
函数时参数preVal
的值。若指定了初始值initialValue
,则curVal
则将使用数组第一个元素;否则preVal
将使用数组第一个元素,而curVal
将使用数组第二个元素。 返回值:使用 “reducer” 回调函数遍历整个数组后的结果。
Array.prototype.reduce1 = function(callback, initialValue) {
if(this == null) {
throw new TypeError('this is null or not defined');
}
if(typeof callback !== "function") {
throw new TypeError(callback + 'is not a function');
}
const O = Object(this);
const len = O.length >>> 0;
let k = 0;
let accumulator = initialValue;
// 如果第二个参数为undefined的情况下,则数组的第一个有效值(非empty)作为累加器的初始值
if(accumulator === undefined) {
while(k < len && !(k in O)) {
k++;
}
// 如果超出数组界限还没有找到累加器的初始值,则TypeError
if(k >= len) {
throw new TypeError('Reduce of empty array with no initial value');
}
accumulator = O[k++];
}
while(k < len) {
if(k in O) {
accumulator = callback(accumulator, O[k], k, O);
}
k++;
}
return accumulator;
}
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。
解决来之前在请求中回调请求产生的回调地狱,使得现在的代码更加合理更加优雅,也更加容易定位查找问题。
fn.length
是个不变的常数)// 写法1-不保存参数,递归局部函数
function curry(fn) {
let judge = (...args) => {
// 递归结束条件
if(args.length === fn.length) return fn(...args);
return (...arg) => judge(...args, ...arg);
}
return judge;
}
// 写法2-保存参数,递归整体函数
function curry(fn) {
// 保存参数,除去第一个函数参数
let presentArgs = [].slice.call(arguments, 1);
// 返回一个新函数
return function(){
// 新函数调用时会继续传参
let allArgs = [...presentArgs, ...arguments];
// 递归结束条件
if(allArgs.length === fn.length) {
// 如果参数够了,就执行原函数
return fn(,,,allArgs);
}
// 否则继续柯里化
else return curry(fn, ...allArgs);
}
}
// 测试
function add(a, b, c, d) {
return a + b + c + d;
}
console.log(add(1, 2, 3, 4));
let addCurry = curry(add);
// 以下结果都返回 10
console.log(addCurry(1)(2)(3)(4));
console.log(addCurry(1)(2, 3, 4));
console.log(addCurry(1, 2)(3)(4));
console.log(addCurry(1, 2)(3, 4));
console.log(addCurry(1, 2, 3)(4));
console.log(addCurry(1, 2, 3, 4));
<!-- 响应式原理的改变 Vue3.x 使用Proxy取代 Vue2.x 版本的Object.defineProperty -->
<!-- 组件选项声明方式Vue3.x 使用Composition API setup 是Vue3.x新增的一个选项,他
是组件内使用Composition API 的入口 -->
<!-- 模板语法变化slot具名插槽语法 自定义指令 v-model 升级 -->
<!-- 其它方面的更改Suspense支持Fragment(多个根节点) 和Protal (在dom其他部分渲染组建内容)组件
针对一些特殊的场景做了处理。基于treeshaking优化,提供了更多的内置功能。 -->
复制代码
为尽快完成首次渲染,我们需要最大限度减小以下三种可变因素:
(1)关键资源的数量。
(2)关键路径长度。
(3)关键字节的数量。
关键资源是可能阻止网页首次渲染的资源。这些资源越少,浏览器的工作量就越小,对 CPU 以及其他资源的占用也就越少。同样,关键路径长度受所有关键资源与其字节大小之间依赖关系图的影响:某些资源只能在上一资源处理完毕之后才能开始下载,并且资源越大,下载所需的往返次数就越多。最后,浏览器需要下载的关键字节越少,处理内容并让其出现在屏幕上的速度就越快。要减少字节数,我们可以减少资源数(将它们删除或设为非关键资源),此外还要压缩和优化各项资源,确保最大限度减小传送大小。
优化关键渲染路径的常规步骤如下:
(1)对关键路径进行分析和特性描述:资源数、字节数、长度。
(2)最大限度减少关键资源的数量:删除它们,延迟它们的下载,将它们标记为异步等。
(3)优化关键字节数以缩短下载时间(往返次数)。
(4)优化其余关键资源的加载顺序:您需要尽早下载所有关键资产,以缩短关键路径长度
以 iPhone XS 为例,当写 CSS 代码时,针对于单位 px,其宽度为 414px & 896px,也就是说当赋予一个 DIV元素宽度为 414px,这个 DIV 就会填满手机的宽度;
而如果有一把尺子来实际测量这部手机的物理像素,实际为 1242*2688 物理像素;经过计算可知,1242/414=3,也就是说,在单边上,一个逻辑像素=3个物理像素,就说这个屏幕的像素密度为 3,也就是常说的 3 倍屏。
对于图片来说,为了保证其不失真,1 个图片像素至少要对应一个物理像素,假如原始图片是 500300 像素,那么在 3 倍屏上就要放一个 1500900 像素的图片才能保证 1 个物理像素至少对应一个图片像素,才能不失真。 当然,也可以针对所有屏幕,都只提供最高清图片。虽然低密度屏幕用不到那么多图片像素,而且会因为下载多余的像素造成带宽浪费和下载延迟,但从结果上说能保证图片在所有屏幕上都不会失真。
还可以使用 CSS 媒体查询来判断不同的像素密度,从而选择不同的图片:
my-image { background: (low.png); }
@media only screen and (min-device-pixel-ratio: 1.5) {
#my-image { background: (high.png); }
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。