手牵手之前端知识积累大全"/>
手牵手之前端知识积累大全
css
盒子模型
css盒子模型 又称框模型 (Box Model) ,包含了元素内容(content)、内边距(padding)、边框(border)、外边距(margin)几个要素
css 外边距合并(叠加)
两个上下方向相邻的元素框垂直相遇时,外边距会合并,合并后的外边距的高度等于两个发生合并的外边距中较高的那个边距值,如图:
box-sizing属性介绍
box-sizing : content-box|border-box|inherit;
- content-box:默认值,可以使设置的宽度和高度值应用到元素的内容框。盒子的width只包含内容。即总宽度=margin+border+padding+width
- border-box: 设置的width值其实是除margin外的border+padding+element的总宽度。盒子的width包含border+padding+内容.即总宽度=margin+width
- inherit : 规定应从父元素继承 box-sizing 属性的值
box-sizing: border-box的典型应用:创建一个width为100%,border为10px的div。问题:不设置box-sizing的情况下,div会溢出。解决方法:设置box-sizing: border-box。#square{width:100%;height:100px;border:10px solid grey;box-sizing:border-box;background:red;
}
用盒子模型画三角形
<!DOCTYPE html>
<html><head><style>.triangle {width : 0;height: 0;border : 100px solid transparent;border-top : 100px solid blue; /*这里可以设置border的top、bottom、left、right四个方向的三角*/}</style></head><body><div class="triangle"></div></body>
</html>
页面显示结果为:
BFC
- 什么是BFC:BFC全称叫做(Block Formatting Context)中文叫块级格式化上下文,是一个网页的概念。
- 怎么触发BFC
- 浮动元素:float 除 none 以外的值。
- 绝对定位元素:position (absolute、fixed)。
- display 为 inline-block、table-cells、flex。
- overflow 除了 visible 以外的值 (hidden、auto、scroll)。
- 遇到哪些问题需要用到BFC
在网页制作过程中由于浏览器加载是自上而下的原因(也可能是其他原因,个人是这么理解),外边距上下会取最大值,左右不影响,所以会导致以下局面。
html
<div class="demo1"></div>
<div class="demo2"></div>
css
.demo1{width: 200px;height: 200px;background-color: red;margin-bottom: 20px;}
.demo2{width: 200px;height: 200px;background-color: black;margin-top: 30px;}
浏览器解析的时候会使外边距叠加在一起,这时候就是遇到了BFC的问题,那么就要通过触发BFC来解决这个问题。
html<div class="demo1"></div><div class="box"><div class="demo2"></div></div>
css
.box{position :absolute;}
.demo1{width: 200px;height: 200px;background-color: red;margin-bottom: 20px;}
.demo2{width: 200px;height: 200px;background-color: black;margin-top: 30px;}
给其中一个子元素讨一个DIV,通过给这个DIV设置属性触发BFC就可以解决问题。
- 高度塌陷
html
<div class="box"><div class="demo"></div>
</div>
css
.box{width: 300px;height: 300px;background-color: black;}
.demo{width: 100px;height: 100px;background-color: red;margin: 20px;}
这个时候红色DIV左右外边距并没有重叠 但是上外边距和父级重叠在了一起,这时候就要触发BFC来解决这个问题
html
<div class="box"><div class="demo"></div>
</div>
css
.box{width: 300px;height: 300px;background-color: black;overflow: hidden;}
.demo{width: 100px;height: 100px;background-color: red;margin: 20px;}
伪类与伪元素
- 常见伪类
伪类用于定义元素的特殊状态。/* 未访问的链接 */
a:link {color: #FF0000;
}/* 已访问的链接 */
a:visited {color: #00FF00;
}/* 鼠标悬停链接 */
a:hover {color: #FF00FF;
}/* 已选择的链接 */
a:active {color: #0000FF;
}
- 所有 CSS 伪元素
选择器 | 例子 | 例子描述 |
---|---|---|
::after | p::after | 在每个 <p> 元素之后插入内容。 |
::before | p::before | 在每个 <p> 元素之前插入内容。 |
::first-letter | p::first-letter | 选择每个 <p> 元素的首字母。 |
::first-line | p::first-line | 选择每个 <p> 元素的首行。 |
::selection | p::selection | 选择用户选择的元素部分。 |
元素定位(position属性值)有哪些
- absolute绝对定位
相对位置为父元素为非static的第一个父元素进行定位。
- fixed 固定定位(老IE6不支持)
相对于浏览器窗口进行定位。
- relative相对定位
相对于其正常(默认布局)位置进行定位。
- static
默认值。没有定位,元素出现在正常的流中(忽略 top, bottom, left, right z-index 声明)
自适应方案
- rem布局
缺点:在响应式布局中,必须通过js来动态控制根元素font-size
的大小,也就是说css样式和js代码有一定的耦合性,且必须将改变font-size
的代码放在css
样式之前
/*上述代码中将视图容器分为10份,font-size用十分之一的宽度来表示,最后在header标签中执行这段代码,就可以动态定义font-size的大小,从而1rem在不同的视觉容器中表示不同的大小,用rem固定单位可以实现不同容器内布局的自适应。*/
function refreshRem() {var docEl = doc.documentElement;var width = docEl.getBoundingClientRect().width;var rem = width / 10;docEl.style.fontSize = rem + 'px';flexible.rem = win.rem = rem;
}
win.addEventListener('resize', refreshRem);
REM
布局也是目前多屏幕适配的最佳方式。默认情况下我们html标签的font-size
为16px,我们利用媒体查询,设置在不同设备下的字体大小。
/* pc width > 1100px */
html{ font-size: 100%;}
body {background-color: yellow;font-size: 1.5rem;
}
/* ipad pro */
@media screen and (max-width: 1024px) {body {background-color: #FF00FF;font-size: 1.4rem;}
}
/* ipad */
@media screen and (max-width: 768px) {body {background-color: green;font-size: 1.3rem;}
}
/* iphone6 7 8 plus */
@media screen and (max-width: 414px) {body {background-color: blue;font-size: 1.25rem;}
}
/* iphoneX */
@media screen and (max-width: 375px) and (-webkit-device-pixel-ratio: 3) {body {background-color: #0FF000;font-size: 1.125rem;}
}
/* iphone6 7 8 */
@media screen and (max-width: 375px) and (-webkit-device-pixel-ratio: 2) {body {background-color: #0FF000;font-size: 1rem;}
}
/* iphone5 */
@media screen and (max-width: 320px) {body {background-color: #0FF000;font-size: 0.75rem;}
}
- 响应式布局的成型方案
现在的css,UI框架等都已经考虑到了适配不同屏幕分辨率的问题,实际项目中我们可以直接使用这些新特性和框架来实现响应式布局。可以有以下选择方案:
- 利用上面的方法自己来实现,比如CSS3 Media Query,rem,vw等
- Flex弹性布局,兼容性较差
- Grid网格布局,兼容性较差
- Columns栅格系统,往往需要依赖某个UI库,如Bootstrap
-
响应式布局的要点
在实际项目中,我们可能需要综合上面的方案,比如用rem
来做字体的适配,用srcset
来做图片的响应式,宽度可以用rem
,flex
,栅格系统等来实现响应式,然后可能还需要利用媒体查询来作为响应式布局的基础,因此综合上面的实现方案,项目中实现响应式布局需要注意下面几点:
- 设置viewport
- 媒体查询
- 字体的适配(字体单位)
- 百分比布局
- 图片的适配(图片的响应式)
- 结合flex,grid,BFC,栅格系统等已经成型的方案
- vue工程项目中如何处理响应式布局
手牵手系列之vue自适应插件px2rem_君子不器-CSDN博客_vue自适应插件
标注:可以结合VSC扩展中提供的cssrem进行计算rem
link 和@import 的区别
- link属于HTML标签,而@import是CSS提供的;
- 页面被加载的时,link会同时被加载,而@import引用的CSS会等到页面被加载完再加载;
- import只在IE5以上才能识别,而link是HTML标签,无兼容问题;
- link方式的样式的权重 高于@import的权重.
html
sessionStorage、localStorage、cookie 区别
javascript
Object操作方法总结
- 创建对象并添加成员
// 最简单的方法(即Object Literal,对象字面变量),之后便可以向它添加属性。// 1. 创建空对象后,在添加属性
const obj = { }
obj.uname = 'dengke'
obj.fn = () => {console.log('ggg')
}
console.log(obj) // { uname: 'dengke', fn: ƒ }// 2. 创建对象并且直接添加属性 (常用)
const obj1 = {uname:'dengke',fn: () => {console.log('ggg')}
}
console.log(obj1) // { uname: "dengke", fn: ƒ }
- 对象浅拷贝
// 扩展运算符(spread)是三个点(...)也可以创建对象(返回一个新对象),注意这是一个浅拷贝
const obj = { name: 'dengke' }
const obj1 = { age: 18,temp: {a: 10}
}const obj2 = { ...obj, ...obj1 }
console.log(obj2) // { name: 'dengke', age: 18, temp: { a: 10 } }obj2.temp.a = 20
console.log(obj2) // { name: 'dengke', age: 18, temp: { a: 20 } }
- 访问对象属性
const obj = {info: 'wakaka',inner: {a: 10,b: 20},arr: [1, 2],sayHi: (name) => {console.log(`hi,${name}`)}
}
// 用 dot(点 .) 的方式访问
console.log(obj.info) // wakaka
console.log(obj.inner) // {"a":10,"b":20}// 用 [] 的方式访问
console.log(obj['info']) // wakaka
console.log(obj['inner']) // {"a":10,"b":20}
补充:如果要访问的对象不存在,可以使用 逻辑运算符 || 指定默认值
console.log(obj.age || 18) // 18
有一个冷门运算符??可以判断undefined和null,这样是比较符合普遍需求的。
const age = 0
const a = age ?? 123
console.log(a) // 0
可选链式操作符(?.) (es11)
// 这是当对象上没有这个键的时候,不会报错,而是赋值undefined
const foo = { name: "zengbo" }
let a = foo.name?.toUpperCase() // "ZENGBO"
let b = foo.name?.firstName?.toUpperCase() // "undefined"
- 删除对象属性
const o = {p: 10,m: 20
}
delete o.p
console.log(o) // { m: 20 }
// 删除对象的属性后,在访问返回 undefined
console.log(o.p) // undefined
- 作为函数参数
const displayPerson = (person) => {console.log(`name: ${person.name || '无名氏'}`)console.log(`age: ${person['age'] || 0}`)
}displayPerson({ name: 'dengke', age: 18 })
// name: dengke
// age: 18displayPerson({ })
// name: 无名氏
// age: 0
- 枚举对象的属性
- for in: 会遍历对象中所有的可枚举属性(包括自有属性和继承属性)
- Object.keys(): 会返回一个包括所有的可枚举的自有属性的名称组成的数组
- Object.getOwnPropertyNames(): 会返回自有属性的名称 (不管是不是可枚举的)
1. for...in
会遍历对象中所有的可枚举属性(包括自有属性和继承属性)
const obj = {itemA: 'itemA',itemB: 'itemB'
}// 使用Object.create创建一个原型为obj的对象 (模拟继承来的属性)
var newObj = Object.create(obj) newObj.newItemA = 'newItemA'
newObj.newItemB = 'newItemB'for(i in newObj){console.log(i)
}
// newItemA
// newItemB
// itemA
// itemB// 现在我们将其中的一个属性变为不可枚举属性
Object.defineProperty(newObj, 'newItemA', {enumerable: false
})for(i in newObj){console.log(i)
}
// newItemB
// itemA
// itemB
补充:如果不想让for...in
枚举继承来的属性可以借助Object.prototype.hasOwnProperty()
// 接上例
for(i in newObj){if( newObj.hasOwnProperty(i) ) console.log(i)
}
// newItemB
2. Object.keys()
: 会返回一个包括所有的可枚举的自有属性的名称组成的数组
// 接上例
const result = Object.keys(newObj)console.log(result) // ["newItemB"]
3. Object.getOwnPropertyNames()
会返回自有属性的名称 (不管是不是可枚举的)
// 接上例
const result = Object.keys(newObj)console.log(result) // ['newItemA','newItemB']
复制代码
- 数据类型检测
const fn = function(n){console.log(n)
}
const str = 'string'
const arr = [1,2,3]
const obj = {a:123,b:456
}
const num = 1
const b = true
const n = null
const u = undefinedconsole.log(typeof str) // string
console.log(typeof arr) // object
console.log(typeof obj) // object
console.log(typeof num) // number
console.log(typeof b) // boolean
console.log(typeof n) // object null是一个空的对象
console.log(typeof u) // undefined
console.log(typeof fn) // function
我们经常会把这个封装成一个函数,使用起来更加方便
/**
* @description: 数据类型的检测
* @param {any} data 要检测数据类型的变量
* @return {string} type 返回具体的类型名称【小写】
*/
const isTypeOf = (data) => {return Object.prototype.toString.call(data).replace(/\[object (\w+)\]/, '$1').toLowerCase()
}console.log(isTypeOf({})) // object
console.log(isTypeOf([])) // array
console.log(isTypeOf("ss")) // string
console.log(isTypeOf(1)) // number
console.log(isTypeOf(false)) // boolean
console.log(isTypeOf(/w+/)) // regexp
console.log(isTypeOf(null)) // null
console.log(isTypeOf(undefined)) // undefined
console.log(isTypeOf(Symbol("id"))) // symbol
console.log(isTypeOf(() => { })) // function
- Object常用的API
1. Object.assign()
Object.assign()
方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。常用来合并对象。
const obj1 = { a: 1, b: 2 }
const obj2 = { b: 4, c: 5 }const obj3 = Object.assign(obj1, obj2)const obj4 = Object.assign({}, obj1) // 克隆了obj1对象console.log(obj1) // { a: 1, b: 4, c: 5 } 对同名属性b进行了替换 obj1发生改变是因为obj2赋给了obj1console.log(obj2) // { b: 4, c: 5 }console.log(obj3) // { a: 1, b: 4, c: 5 }console.log(obj4) // { a: 1, b: 4, c: 5 }
注意
-
如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。
-
Object.assign
方法只会拷贝源对象自身的并且可枚举的属性到目标对象。 -
assign其实是浅拷贝而不是深拷贝
2. Object.keys()
const arr = ['a', 'b', 'c']
console.log(Object.keys(arr)) // ['0', '1', '2']const obj = { 0: 'a', 1: 'b', 2: 'c' }
console.log(Object.keys(obj)) // ['0', '1', '2']const obj2 = { 100: 'a', 2: 'b', 7: 'c' }
console.log(Object.keys(obj2)) // ['2', '7', '100']
3. Object.values()
Object.values()
方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for...in
循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。与Object.keys()
相似,区别在于这个返回的是数据的值也就是value
const obj1 = { foo: 'bar', baz: 42 }
console.log(Object.values(obj1)) // ['bar', 42]const obj2 = { 0: 'a', 1: 'b', 2: 'c' }
console.log(Object.values(obj2)) // ['a', 'b', 'c']
注意
-
对象
key
为number
的话,会从升序枚举返回。
const obj3 = { 100: 'a', 2: 'b', 7: 'c' }
console.log(Object.values(obj3)) // ['b', 'c', 'a']
复制代码
4. Object.entries(obj)
Object.entries()
方法返回一个给定对象自身可枚举属性的键值对数组。可使用Object.fromEntries()
方法,相当于反转了Object.entries()
方法返回的数据结构。接下来也会介绍Object.fromEntries()
const obj1 = {name: 'dengke',age: 18
};for (const [key, value] of Object.entries(obj1)) {console.log(`${key}: ${value}`);
}
// "name: dengke"
// "age: 18"const obj2 = { foo: 'bar', baz: 42 }
console.log(Object.entries(obj2)) // [ ['foo', 'bar'], ['baz', 42] ]const obj3 = { 0: 'a', 1: 'b', 2: 'c' }
console.log(Object.entries(obj3)) // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
补充
-
将
Object
转换为Map
,new Map()
构造函数接受一个可迭代的entries
。借助Object.entries
方法你可以很容易的将Object
转换为Map
:
const obj = { foo: "bar", baz: 42 }
const map = new Map(Object.entries(obj))
console.log(map) // Map { foo: "bar", baz: 42 }
5. Object.fromEntries()
const entries = new Map([['foo', 'bar'],['baz', 42]
]);const obj = Object.fromEntries(entries);console.log(obj);
// Object { foo: "bar", baz: 42 }
补充
-
Map
转化为Object
通过 Object.fromEntries
, 可以将Map
转换为Object
:
const map = new Map([ ['foo', 'bar'], ['baz', 42] ])
const obj = Object.fromEntries(map)
console.log(obj)
// { foo: "bar", baz: 42 }
-
Array
转化为Object
通过 Object.fromEntries
, 可以将Array
转换为Object
:
const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
const obj = Object.fromEntries(arr)
console.log(obj)
// { 0: "a", 1: "b", 2: "c" }
-
对象转换
Object.fromEntries
是与 Object.entries()
相反的方法,用 数组处理函数 可以像下面这样转换对象:
const object1 = { a: 1, b: 2, c: 3 }const object2 = Object.fromEntries(Object.entries(object1).map(([ key, val ]) => [ key, val * 2 ])
)// Object.entries(object1) >>> [["a",1],["b",2],["c",3]]console.log(object2) // { a: 2, b: 4, c: 6 }
6. Object.prototype.hasOwnProperty()
hasOwnProperty()
方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
const obj1 = {};
obj1.property1 = 42console.log(obj1.hasOwnProperty('property1')) // true
console.log(obj1.hasOwnProperty('toString')) // false
console.log(obj1.hasOwnProperty('hasOwnProperty')) // false
注意
-
只会对自身属性进行判断,继承来的一律返回
false
。配合for...in
使用,可以避免其遍历继承来的属性。
const o = new Object()
o.prop = 'exists'console.log(o.hasOwnProperty('prop')) // true
console.log(o.hasOwnProperty('toString')) // false
console.log(o.hasOwnProperty('hasOwnProperty')) // false
-
即使属性的值是
null
或undefined
,只要属性存在,hasOwnProperty
依旧会返回true
。
const o = new Object();
o.propOne = null
o.propTwo = undefinedconsole.log(o.hasOwnProperty('propOne')) // true
console.log(o.hasOwnProperty('propTwo')) // true
7. Object.getOwnPropertyNames()
const arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr).sort()) // ["0", "1", "2", "length"]// 类数组对象
const obj = { 0: "a", 1: "b", 2: "c"};
console.log(Object.getOwnPropertyNames(obj).sort()) // ["0", "1", "2"]// 使用Array.forEach输出属性名和属性值
Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) {console.log(val + " -> " + obj[val]);
})
// 0 -> a
// 1 -> b
// 2 -> c// 不可枚举属性
const my_obj = Object.create({}, {getFoo: {value: function() { return this.foo; },enumerable: false}
});
my_obj.foo = 1;console.log(Object.getOwnPropertyNames(my_obj).sort())
// ["foo", "getFoo"]
8. Object.freeze()
Object.freeze()
方法可以冻结一个对象。
const obj = {prop: 42
}Object.freeze(obj)
obj.prop = 33
console.log(obj.prop)
// 42
补充
-
被冻结的对象是不可变的。但也不总是这样。下例展示了冻结对象不是常量对象(浅冻结)。
const obj1 = {internal: {}
}
Object.freeze(obj1)obj1.internal.a = 'aValue'
console.log(obj1.internal.a) // 'aValue'
-
要使对象不可变,需要递归冻结每个类型为对象的属性(深冻结)。
// 深冻结函数.
function deepFreeze(obj) {// 取回定义在obj上的属性名const propNames = Object.getOwnPropertyNames(obj)// 在冻结自身之前冻结属性propNames.forEach(function(name) {const prop = obj[name]// 如果prop是个对象,冻结它if (typeof prop == 'object' && prop !== null)deepFreeze(prop)})// 冻结自身return Object.freeze(obj);
}const obj2 = {internal: {}
}deepFreeze(obj2)
obj2.internal.a = 'anotherValue'
obj2.internal.a // undefined
9. Object.isFrozen()
Object.isFrozen()
方法判断一个对象是否被冻结
。
// 一个对象默认是可扩展的, 所以它也是非冻结的。
Object.isFrozen({}) // false// 一个不可扩展的空对象同时也是一个冻结对象。
var vacuouslyFrozen = Object.preventExtensions({})
Object.isFrozen(vacuouslyFrozen) // truevar frozen = { 1: 81 }
Object.isFrozen(frozen) // false// 使用Object.freeze是冻结一个对象最方便的方法.
Object.freeze(frozen)
Object.isFrozen(frozen) // true
this
- 实质
var obj = { foo: 5 };
上面的代码将一个对象赋值给变量obj
。JavaScript 引擎会先在内存里面,生成一个对象{ foo: 5 }
,然后把这个对象的内存地址赋值给变量obj
。也就是说,变量obj
是一个地址(reference)。后面如果要读取obj.foo
,引擎先从obj
拿到内存地址,然后再从该地址读出原始的对象,返回它的foo
属性。
var f = function () {console.log(this.x);
}var x = 1;
var obj = {f: f,x: 2,
};// 单独执行
f() // 1// obj 环境执行
obj.f() // 2
上面代码中,函数f
在全局环境执行,this.x
指向全局环境的x
;在obj
环境执行,this.x
指向obj.x
。
- 使用场合
(1)全局环境
全局环境使用this
,它指的就是顶层对象window
。
this === window // truefunction f() {console.log(this === window);
}
f() // true
(2)构造函数
构造函数中的this
,指的是实例对象。
var Obj = function (p) {this.p = p;
};// 上面代码定义了一个构造函数Obj。由于this指向实例对象,所以在构造函数内部定义this.p,就相当于定义实例对象有一个p属性。var o = new Obj('Hello World!');
o.p // "Hello World!"
(3)对象的方法
如果对象的方法里面包含this
,this
的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变this
的指向。
var obj ={foo: function () {console.log(this);}
};obj.foo() // obj
var a = {p: 'Hello',b: {m: function() {console.log(this.p);}}
};a.b.m() // undefined// 转成var a = {b: {m: function() {console.log(this.p);},p: 'Hello'}
};
如果这时将嵌套对象内部的方法赋值给一个变量,this
依然会指向全局对象。
var a = {b: {m: function() {console.log(this.p);},p: 'Hello'}
};var hello = a.b.m;
hello() // undefined
上面代码中,m
是多层对象内部的一个方法。为求简便,将其赋值给hello
变量,结果调用时,this
指向了顶层对象。为了避免这个问题,可以只将m
所在的对象赋值给hello
,这样调用时,this
的指向就不会变。
var hello = a.b;
hello.m() // Hello
由于this
的指向是不确定的,所以切勿在函数中包含多层的this
。
var o = {f1: function () {console.log(this);var f2 = function () {console.log(this);}();}
}o.f1()
// Object
// Window// 上面代码包含两层this,结果运行后,第一层指向对象o,第二层指向全局对象,因为实际执行的是下面的代码。
var temp = function () {console.log(this);
};var o = {f1: function () {console.log(this);var f2 = temp();}
}
一个解决方法是在第二层改用一个指向外层this
的变量。
var o = {f1: function() {console.log(this);var that = this;var f2 = function() {console.log(that);}();}
}o.f1()
// Object
// Object
- 避免数组处理方法中的 this
var o = {v: 'hello',p: [ 'a1', 'a2' ],f: function f() {this.p.forEach(function (item) {console.log(this.v + ' ' + item);});}
}o.f()
// undefined a1
// undefined a2解决这个问题的一种方法,就是前面提到的,使用中间变量固定this。var o = {v: 'hello',p: [ 'a1', 'a2' ],f: function f() {var that = this;this.p.forEach(function (item) {console.log(that.v+' '+item);});}
}o.f()
// hello a1
// hello a2// 另一种方法是将this当作foreach方法的第二个参数,固定它的运行环境。
var o = {v: 'hello',p: [ 'a1', 'a2' ],f: function f() {this.p.forEach(function (item) {console.log(this.v + ' ' + item);}, this);}
}o.f()
// hello a1
// hello a2
- 绑定 this 的方法
(1)Function.prototype.call()
函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。var obj = {};var f = function () {return this;
};f() === window // true
f.call(obj) === obj // truecall方法的参数,应该是一个对象。如果参数为空、null和undefined,则默认传入全局对象。var n = 123;
var obj = { n: 456 };function a() {console.log(this.n);
}a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(obj) // 456
call
方法还可以接受多个参数。
func.call(thisValue, arg1, arg2, ...)
call
的第一个参数就是this
所要指向的那个对象,后面的参数则是函数调用时所需的参数。
function add(a, b) {return a + b;
}add.call(this, 1, 2) // 3
(2)Function.prototype.apply()
apply
方法的作用与call
方法类似,也是改变this
指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下。
func.apply(thisValue, [arg1, arg2, ...])
apply
方法的第一个参数也是this
所要指向的那个对象,如果设为null
或undefined
,则等同于指定全局对象。第二个参数则是一个数组,该数组的所有成员依次作为参数,传入原函数。原函数的参数,在call
方法中必须一个个添加,但是在apply
方法中,必须以数组形式添加。
function f(x, y){console.log(x + y);
}f.call(null, 1, 1) // 2
f.apply(null, [1, 1]) // 2
- 转换类似数组的对象
Array.prototype.slice.apply({0: 1, length: 1}) // [1]
Array.prototype.slice.apply({0: 1}) // []
Array.prototype.slice.apply({0: 1, length: 2}) // [1, undefined]
Array.prototype.slice.apply({length: 1}) // [undefined]
(3)Function.prototype.bind()
bind()
方法用于将函数体内的this
绑定到某个对象,然后返回一个新函数。
bind方法的参数就是所要绑定this的对象,下面是一个更清晰的例子。var counter = {count: 0,inc: function () {this.count++;}
};var func = counter.inc.bind(counter);
func();
counter.count // 1
上面代码中,bind()
方法将inc()
方法内部的this
,绑定到obj
对象。结果调用func
函数以后,递增的就是obj
内部的count
属性。
var counter = {count: 0,inc: function () {this.count++;}
};var obj = {count: 100
};
var func = counter.inc.bind(obj);
func();
obj.count // 101
var add = function (x, y) {return x * this.m + y * this.n;
}var obj = {m: 2,n: 2
};var newAdd = add.bind(obj, 5);
newAdd(5) // 20
this 关键字 - JavaScript 教程 - 网道
es6
promise
- 什么是promise
es6提供的新的处理异步编程的解决方案。
- 为什么使用promise
支持链式调用,可以解决回调地狱问题 。
- 什么是回调地狱
回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件。
- 回调地狱的缺点
- 不便于阅读
- 不便于异常处理
// promise初体验
const p = new Promise((resolve) => {resolve('成功')
}, (rejuct) => {rejuct('失败')
})p.then((res) => {console.log(res) // 成功
}).catch((err)=>{console.log(err) // 失败
})
// fs文件读取const fs = require('fs')let p = new Promise((resolve, reject) => {fs.readFile('./resource/content.txt', (err, data) => {// 如果失败if (err) reject(err);//如果成功resolve(data)})
})// 调用then
p.then(value => {console.log(value.toString())
}, reason => {console.log(reason)
})
let、const、var的区别
- var是ES5提出的,let和const是ES6提出的。
const声明的是常量,必须赋值
1)一旦声明必须赋值,不能使用null占位。
2)声明后不能再修改
3)如果声明的是复合类型数据(对象、数组),可以修改其属性
- let和var声明的是变量,声明之后可以更改,声明时可以不赋值
- var允许重复声明变量,后一个变量会覆盖前一个变量。let和const在同一作用域不允许重复声明变量,会报错。
- var声明的变量存在变量提升(将变量提升到当前作用域的顶部)。即变量可以在声明之前调用,值为undefined。
- let和const不存在变量提升。即它们所声明的变量一定要在声明后使用,否则报ReferenceError错。
- var不存在块级作用域。let和const存在块级作用域。
- ES5中作用域有:全局作用域、函数作用域。没有块作用域的概念。
- ES6(简称ES6)中新增了块级作用域。块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域。
解构赋值
- 数组解构
const F4 = ['小沈阳','刘能','赵四','宋小宝']let [xiao,liu,zhao,song] = F4
const.log(xiao) // 小沈阳
const.log(liu) // 刘能
const.log(zhao) // 赵四
const.log(song) // 宋小宝
- 对象解构
const zhao={name:'赵本山',age:66,xiaoping:function(){console.log('我可以演小品')}
}let {name,age,xiaoping} = zhao
模板字符串
1、声明
let str = `我也是一个字符串`2、内容中可以直接出现换行符
let str = `<ul><li>沈腾</li><li>沈腾</li><li>沈腾</li><li>沈腾</li></ul>`;3、变量拼接
let lovest = '魏翔'
let out = `${lovest}是我心中最搞笑的演员!!`
对象简化写法
let name = 'superman'
let change = function(){console.log('我们可以改变你')
}
const school = {name,change
}
箭头函数
1、this是静态的,this始终指向函数声明时所在作用于下的this值
2、箭头函数适合与this无关的回调,定时器,数组方法回调
3、箭头函数不适合与this有关的回调,事件回调,对象方法回调
// ES6允许使用【箭头】(=>)定义函数
let fn = (a,b)=>{return a+b
}1、 this指向问题
function getName(){console.log(this.name)
}
let getName2 = () =>{console.log(this.name)
}// 设置window对象的name属性
window.name = 'superman'
const school = {name = 'nanjing'
}
// 调用getName(); // superman
getName2(); // superman// call方法调用
getName.call(school) // superman
getName2.call(school) // nanjing2、不能作为构造实例化对象
let person = (name,age)=>{this.name = name;this.age = age;
}
let me = new Person('xiao',30) // 报错 Person is not a constructor3、不能使用arguments变量
let fn = () =>{console.log(arguments)
}
fn(1,2,3) // 报错 arguments is not defined4、箭头函数的简写4.1 省略小括号,当形参有且只有一个的时候let add = n =>{return n+n}4.2 省略花括号,当代码体只有一条语句的时候,此时return必须省略let pow = n => n*n
5、对象方法回调{name:'superman',getName:()=>{console.log(this.name) // undefined,不在指向函数内部,指向外层window}}
rest参数
// es5获取实参的方式function date(){console.loga(arguments)
}
date('柏芝','阿娇','思慧')// rest 参数
// rest 参数必须要放到参数最后
function date(...args){console.log(args) // 输出数组,可以使用filter some every map
}date('阿娇','柏芝','思慧')
扩展运算符
- 扩展运算符能将【数组】转换为逗号分隔的【参数序列】
const tfboys = ['易烊千玺','王源','王俊凯']// 声明一个函数
function chunwan(){console.log(arguments)
} chunwan(...tfboys)
- 数组合并
const kuaizi = ['王太利','肖央']
const fenghuang = ['曾毅','玲花']
const zuixyuanxiaopingguo = [...kuaizi,...fenghuang]
console.log(zuixyuanxiaopingguo)
- 数组克隆
const sanzhihua = ['E','G','M']
const sanyecao = [...sanzhihua]
console.log(sanyecao) // 浅拷贝
- 伪数组转为真正数组
const divs = document.querySelectAll('div')
const divArr = [...divs]
Symbol
定义:Es6引入了一种新的原始数据类型Symbol,表示独一无二的值。他是JavaScript语言中第七种数据类型,是一种类似于字符串的数据类型。
- 特点
- Symbol的值是唯一的,用来解决命名冲突的问题
- Symbol不是与其他数据进行运算
- Symbol定义的对象属性不能使用for...in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名
// 创建Symbol
let s = Symbol()
console.log(s,typeOf s)let s2 = Symbol('张三')
let s3 = Symbol('张三')
console.log(s2 === s3) // false// Symbol for 创建
let s4 = Symbol.for('尚硅谷')
let s5 = Symbol.for('尚硅谷')
console.log(s4 === s5) // true// 不能与其他数据类型进行运算
let result = s + 100 // 报错
let result = s > 100 // 报错
let result = s + '100' // 报错// 数据类型总结
undefined 、 string 、 Symbol 、 number 、 null 、 Object 、 Boolean
迭代器
- for...of
// 声明一个对象,用面向对象的方式
const banji = {name:'终极一班',stus:['小明','小宁','小天','kning'],[Symbol.iterator](){let index = 0;return{next:()=>{if(index<this.stus.length){const result = {value:this.stus[i],done:false}// 下标自增index++;return result}else{return{value:undefined,done:true}}}}}
}// 遍历这个对象
for(let v of banji){console.log(v)
}
生成器
生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同
// 生成器其实就是一个特殊的函数
// 异步编程 纯回调函数 node fs ajax mongodbfunction * gen(){console.log(111)yieid '一只没有耳朵'console.log(222)yieid '一只没有尾巴'console.log(333)yieid '真奇怪'console.log(444)
}let iterator = gen()
iterator.next() // 111
iterator.next() // 222
iterator.next() // 333
iterator.next() // 444// 遍历
for(let v of gen()){console.log(v) //一只没有耳朵 、一只没有尾巴、真奇怪
}// 实操
function * gen(){yieid '一只没有耳朵'yieid '一只没有尾巴'yieid '真奇怪'
}
let iterator = gen()
console.log(iterator.next()) // {value:'一只没有耳朵',done:false}
console.log(iterator.next()) // {value:'一只没有尾巴',done:false}
console.log(iterator.next()) // {value:'真奇怪',done:false}
console.log(iterator.next()) // {value:undefined,done:true}// 生成器传参
function * gen(arg){console.log(arg) // AAAlet one = yieid 111console.log(one) // BBBlet two yieid 222console.log(two) // CCCyieid 333
}
let iterator = gen('AAA')
console.log(iterator.next()) // {value:111,done:false}
// next方法可以传入实参
console.log(iterator.next('BBB')) // {value:222,done:false}
console.log(iterator.next('CCC')) // {value:333,done:false}
集合(Set)
// 声明一个set
let s = new Set();
let S2 = new Set(['大事儿','小事儿','好事儿','坏事儿','小事儿']) // 自动去重// 元素个数
console.log(s2.size) // 4// 添加新的元素
s2.add('喜事儿')// 删除元素
s2.delete('坏事儿')// 检测
console.log(s2.has('好事儿')) // true// 清空
s2.clear()// 遍历
for(let v of s2){console.log(v) // 大事儿,小事儿,好事儿,坏事儿
}
- set实践
let arr = [1,2,3,4,5,4,3,2,1]// 1、数组去重
let result = [...new Set(arr)];// 2、交集
let arr2 = [4,5,6,5,5]
let result = [...new Set(arr)].filter(item=>{let s2 = new Set(arr2); // 4 5 6if(s2.has(item)){return true}else{return false}
})// 简化
let result = [...new Set(arr)].filter(item=>new Set(arr2).has(item))// 3、并集
let union =[...new set([...arr,...arr2])]// 4、差集
let diff = [...new Set(arr)].filter(item=>!(new Set(arr2).has(item)))
Map介绍与API
// 声明Map
let m = new Map()
// 添加元素
m.set('name','尚硅谷')
m.set('change',function(){console.log("我们可以改变你")
})
let key = {school:'ATGUIGU'
}
m.set(key,['北京','上海','深圳'])// size
console.log(m.size) // 3
// 删除
m.delete('name')
// 获取
console.log(m.get('key')) //['北京','上海','深圳']
// 清空
m.clear()
// 遍历
for(let v of m){console.log(v)
}
class类
- class实现构造函数
// es5 构造函数
function Phone(brand,price){this.brand = brandthis.price= price
}// 添加方法
Phone.prototype.call = function(){console.log('我可以打电话')
}// 实例化对象
let Huawei = new Phone('华为',5999)
Huawei.call()
console.log(Huawei)// es6 class
class Phone{// 构造方法
constructor(brand,price) {this.brand = brandthis.price = price}// 方法必须使用该语法,不能使用ES5的对象完整形式call(){console.log('我可以打电话')}
}let onePlus = new Phone('一加',6999)
console.log(onePlus)
- class继承
// es5继承
function Phone(brand,price){this.brand = brand
}
Phone.prototype.call = function(){console.log('我可以打电话')
}
function SmartPhone(brand,price,color,size){Phone.call(this,brand,price)this.color = colorthis.size = size
}
// 设置子级构造函数
SmartPhone.prototype = new Phone
SmartPhone.prototype.constructor = SmartPhone
SmartPhone.prototype.photo = function(){console.log('我可以拍照')
}
SmartPhone.prototype.play = function(){console.log('我可以玩游戏')
}const chuizi = new SmartPhone('锤子',2499,'黑色','5.5inch')// es6
class Phone{constructor(brand,price){this.brand = brandthis.price = pirce}// 父类的成员属性call(){console.log('我可以打电话')}
}
class SmartPhone extends Phone{// 构造函数constructor(brand,price,color,size){super(brand,price)this.color = colorthis.size = size}phone(){console.log('拍照')}playGame(){console.log('玩手机')}call(){console.log('视频通话')}
}
const xiaomi = new SmartPhone('小米',2499,'黑色','5.5inch')
xiaomi.call() // 视频通话 重写 覆盖子类
xiaomi.photo()
xiaomi.playGame()
- class中的getter、setter
// get setclass Phone{get price(){console.log('价格属性被读取')return 'iloveyou'}set price(newVal){console.log('价格属性被修改了')}}// 实例化对象
let s = new Phone()console.log(s.price) // iloveyous.price = 'free' // 价格属性被修改了
数值扩展
1、 Number.EPSILON是JavaScript表示最小精度function equal(a,b){if(Math.abs(a-b)<Number.EPSILON){return true}else{return false}
}
console.log(0.1+0.2===0.3) // flase
console.log(equal(a-b)) // true2、 二进制和八进制let b = 0b1010 // 二进制0b开头 10
let 0 = 0o777 // 八进制0o开头
let d = 100 // 十进制
let x = 0xff // 十六进制3、Number.isFinite 检测一个数值是否为有限数console.log(Number.isFinite(100)) // true
console.log(Number.isFinite(100/0)) // false
console.log(Number.isFinite(Infinity)) // false4、Number.isNaN 检测一个数值是否为NaNconsole.log(Number.isNaN(123)) // false5、Number.parseInt Number.parseFloat字符串转整数console.log(Number.parseInt('51111214love')) // 51111214
console.log(Number.parseFloat('3.123231环境')) // 3.1232316、Number.isInteger 判断一个数是否为整数console.log(Number.isInteger(5)) // true
console.log(Number.isInteger(2.5)) // false7、Math.trunc 将数字的小数点部分抹掉console.log(Math.trunc(3.5)) // 38、Math.sign 判断一个数到底是正数 负数 还是零console.log(Math.sign(100)) // 1
console.log(Math.sign(100)) // 0
console.log(Math.sign(-100000)) // -1
- 对象方法扩展
1、Object.is 判断两个值是否完全相等console.log(Object.is(120,120)); // true
console.log(Object.is(NaN,NaN)); // true
console.log(NaN === NaN) // false2、Object.assgin 对象的合并const config1={host:'localhost',port:3306,name:'root',pass:'root'
}
const config2={host:'',port:33060,name:'atguigu',pass:'iloveyou'
}Object.assgin(config1,config2) // 后面覆盖前面
Object.assgin({},config1) // 对象浅拷贝3、Onject.setPrototypeOf 设置原型对象const school = {name:'上古遗'
}
const cities = {xiaoqu:['北京','上海','深圳']
}
Onject.setPrototypeOf(school,cities)
console.log(Onject.setPrototypeOf(school)) //xiaoqu:['北京','上海','深圳']
console.log(school) // name:'上古遗'
模块化
模块化的优势有以下几点
- 防止命名冲突
- 代码复用
- 高位户型
ES6之前的模块化规范有:
- CommonJS => NodeJS、Browerify
- AMD => requireJS
- CMD => seaJS
import
// 引入m1.js模块内容
import * as m1 from './m1.js'
console.log(m1) // 导出全部export
// 导出m1.js模块内容
export const name = '哈哈哈'
export function getName(){return 'cc'
}
- export default 默认暴露
ES7新特性
- includes
// includes
const mingzhu = ['西游记','红楼梦','三国演义','水浒传']console.log(mingzhu.includes('西游记')) // true
console.log(mingzhu.includes('金瓶梅')) // false
- 幂运算 **
console.log(2 ** 10) // 1024// 等价于
Math.pow(2,10)
ES8新特性
- async和await
async 和 await两种语法结合可以让异步代码像同步代码
- async函数
- async函数的返回值为promise
- promise对象的结果由async函数执行的返回值决定的
// async 函数async function fn(){return 'Lee'
// 返回的结果不是一个promise类型的对象,则这个函数返回的结果就是一个成功的promise对象// 如果抛出错误,则返回的结果是一个失败的promisethrow new Error('出错了!')// 如果返回结果是一个promise对象return new promise((resolve,reject)=>{// 成功resolve('成功的结果')// 失败reject('失败的错误')})
}const result = fn()// 调用then方法
result.then(value=>{console.log(value) // 成功的结果
},reason=>{console.log(reason) // 失败的错误
})console.log(result)
- await表达式
- await必须写在async函数中
- await右侧的表达式一般为promise对象
- await返回的表达式成功的值
- await的promise失败了,就会抛出异常,需要通过try...catch捕获
// 创建 promise对象
const p = new promise((resolve,reject)=>{// 成功resolve('成功的值!')// 失败reject('失败了!')
})// await 要放在async函数中
async function main(){try{let result = await p console.log(result) // 成功的值}catch(e){console.log(e) // 失败了!}
}// 调用函数
main()
- async与await结合封装ajax请求
function sendAJAX() {return new Promise((resolve, reject) => {// 1、创建对象const x = new XMLHttpRequest();// 2、初始化x.open('GET', url)// 3、发送x.send()// 4、事件绑定x.onreadystatechange = function () {if (x.readyState === 4) {if (x.status >= 200 && x.status < 300) {resolve(x.response)} else {reject(x.status)}}}})
}// promise then 方法测试
sendAJAX('/getJoke').then(value => {}, reason => {})// async 与 awaitasync function main() {let result = await sendAJAX('/getJoke')}
- 对象方法扩展
// 声明对象
const school = {name: 'happy',cities: ['北京', '上海', '深圳', '苏州'],xueke: ['前端', 'java', '大数据']
}// 获取对象的所有键
Object.keys(school)// 获取对象所有的值
Object.values(school)// entries
Object.entries(school)// 方便创建Map
const m = new Map(Object.entries(school))
console.log(m.get('cities'))// 对象属性的描述对象
Object.getOwnPropertyDescriptor(school)const obj = Object.create(null,{name:{// 设置值value:'尚硅谷',// 属性特性writable:true,configurable:true,enumerable:true}
})
ES9新特性
Rest参数与spread扩展运算符在ES6中已经引入,不过ES6中只针对数组
在ES9中为对象提供了像数组一样的rest参数和扩展运算符
// rest 用法
function connect({host,port,...user}){console.log(host,port,user)
}
connect({host:'127.0.0.1',port:3306,username:'root',password:'root'
})// 技能
const skillone={q:'天音波'
}
const skilltwo={w:'金钟罩'
}
const skillthree={e:'天雷破'
}
const skillfour={r:'猛龙摆尾'
}const mangseng = {...skillone,...skilltwo,...skillthree,...skillthree}
console.log(mangseng) // { q:'天音波',w:'金钟罩',e:'天雷破',r:'猛龙摆尾' }
- 正则扩展
// 正向断言
let str = 'JS5211314你知道么555啦啦啦'
const reg = /\d+(?=啦)/;
const result = reg.exec(str)// 反向断言
const reg = /(?<=么)\d+/;
const result = reg.exec(str)
ES10新特性
- Object.fromEntries
// 二维数组
const result = Object.fromEntries([['name','尚硅谷'],['xueke','Java']
])console.log(result) //{name:'尚硅谷',xueke:'Java'}// Map
const m = new Map();
m.set('name',ATGUIGU);
const result = Object.fromEntries(m)
console.log(result) //{name:'ATGUIGU'}// Object.entries Es8
const str = Object.entries({name:'尚硅谷'
})
console.log(str) // ['name','尚硅谷']
- trim
// trimlet str = ' lloveyou 'console.log(str) // ' lloveyou '
console.log(str.trimStart()) // 'lloveyou '
console.log(str.trimEnd()) // ' lloveyou'
- flat与flatMap
// flat 平
// 将多维数组转化为低位数组
const arr = [1,2,3,4,[5,6]]
console.log(arr.flat()) //[1,2,3,4,5,6]const arr = [1,2,3,4,[5,6,[7,8,9]]]
// 参数为深度 是一个数字,默认为1
console.log(arr.flat(2)) //[1,2,3,4,5,6,7,8,9]
// flatMap
const arr = [1,2,3,4]
const result = arr.flatMap(item=> [item*10])
console.log(result) // [10,20,30,40]
- Symbol.prototype.description
// 创建Symbol
let s = Symbol('尚硅谷')
console.log(s.description) // 尚硅谷
ES11新特性
- 私有属性
class Person{// 公有属性name// 私有属性#age#weight// 构造方法constructor(name,age,weight){this.name = namethis.#age = agethis.#weight = weight}intro(){console.log(this.name) // 小红console.log(this.#age) // 18console.log(this.#weight) // 45KG}
}// 实例化
const girl = new Person('小红',18,'45KG')
console.log(this.name)
console.log(this.#age) // 私有属性 报错 error
console.log(this.#weight)
- Promise.allsettled
// 声明两个promise对象
const p1 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve('成功')})
})const p2 = new Promise((resolve,reject)=>{setTimeout(()=>{reject('失败')})
})// 调用allsettled方法
const result = Promise.allSettled([p1,p2]) // 如果有一个成功,都会返回成功,并且两个promise对象的值都会被返回出来// Promise.all
const res = Promise.all([p1,p2]) // 如果其中一个失败 都会失败,并且返回失败的那个promise
- 可选链操作符
// ?.
function main(config){const dbHost = config?.db?.host console.log(dbHost)
}main({db:{host:'192.168.1.100',username:'root'},cache:{host:'192.168.1.200',username:'admin'}
})
vue
$nextTick
$nextTick就是用来知道什么时候DOM更新完成的
更多推荐
手牵手之前端知识积累大全
发布评论