第 16 期 - 连续 7 天熬夜,我总结了关于 JavaScript 和 ES 的 25 个要点!
前言
说起JavaScript,大家都知道是一门脚本语言。那么ES是什么鬼呢?ES全称ECMAScript ,是JavaScript语言的国际标准。
最近总结了条js的基础特性相关的知识点,大家一起看一下吧
1.严格模式
- 使用严格模式,可以在函数内部进行较为严格的全局和局部的错误条件检查
- 严格模式的编译指示,"use strict"
- 创建全局变量,未声明变量,非严格模式下为创建全局变量;严格模式下为抛出ReferenceError
- 对变量调用delete操作符,删除变量,非严格模式下为静默失败;严格模式下为抛出ReferenceError
- 操作对象情况下:a,只读属性赋值会抛出TypeError;b,对不可配置的属性使用delete操作符会抛出TypeError;c,为不可扩展的对象添加属性会抛出TypeError。
- 重名属性情况:a,非严格模式下没有错误,以第二个属性为准;b,严格模式下会抛出语法错误。
- 函数参数必须唯一,重名参数,在非严格模式下没有错误,只能访问第二个参数;严格模式下,会抛出错误。
function funValue(value) {
value="dada";
alert(value); // dada
alert(argument[0]); // 非严格模式:dada
// 严格模式模式 dadaqianduan
}
funValue('dadaqianduan');
- 访问arguments.callee和arguments.caller,在非严格模式下没有问题,严格模式下抛出TypeError。
2.Class基础语法
在JavaScript当中如何声明一个类?如何定义类中的方法?如何实例化对象?
我们来看看下面的代码示例:
// es5
let dada = function(type) {
this.type = type
}
dada.prototype.study = function() {
console.log('魔王哪吒');
}
let da1 = new dada('程序员')
let da2 = new dada('It')
da1.constructor.prototype.study = function() {
console.log('dadaqianduan');
}
da1.study()
JavaScript constructor
属性
定义和用法
constructor
属性返回对创建此对象的数组函数的引用。
语法
object.constructor
constructor
是一种用于创建和初始化class
创建的对象的特殊方法。
// es6
class Da {
constructor(name) { // 构造函数内写属性
this.name = name;
}
eat() { // 构造函数外写方法
console.log('i eat')
}
}
const da1 = new Da('da1');
console.log(da1.name); // da1
console.log(da1);
- 一个类中只能有一个名为“constructor"的方法,出现多次构造函数constructor方法会抛出一个SyntaxError错误
- 在一个构造方法中可以使用super来调用一个父类的构造方法
- 如果没有指定一个构造函数方法constructor方法,就会使用一个默认的构造函数
3.类的属性Setter和Getter
var daObj = {
get val() {
return ;
},
set val(value) {
}
}
get:
var da = {
a: 1,
get val(){
return this.a + 1;
}
}
console.log(da.val);//2
da.val = 100;
console.log(da.val);//2
class Da {
constructor(type) {
this.type = type
}
get age() {
return 1
}
set age(val) {
this.realAge = val
}
eat() {
console.log('i am eat')
}
}
let da1 = new Da('da1')
console.log(da1.age)
da1.age = 1
console.log(da1.realAge)
class Da {
constructor(type, age) {
this.type = type
this.age1 = age
}
get age() {
return this._age
}
set age(val) {
this._age = val
}
}
利用set/get实现对element.innerHTML封装
class myHTMLElement {
constructor(element) {
this.element = element
}
get html() {
return this.element.innerHTML
}
set html(value) {
this.element.innerHTML = value
}
}
设置一个闭包,通过一定的规则来限制对它的修改:
let myName = 'dada'
class Da {
constructor(type) {
this.type = type
}
get name() {
return myName
}
set name(val) {
myName = val
}
}
4.静态方法
在es5中实现的静态方法:
let Da = function (type) {
this.type = type
this.eat = function() {
console.log('i eat')
}
}
Da.study = function(book) {
console.log('i book');
}
let Da = function(type) {
this.type = type
}
Da.prototype.eat = function() {
Da.walk()
console.log('i am')
}
Da.walk = function(){
console.log('walk')
}
let da1 = new Da('da1')
da1.eat()
// walk
// i am
静态方法在你的实例化对象是找不到的
在es6中的静态方法,标记static
class Da {
constructor(type) {
this.type = type
}
eat() {
console.log('i eat')
}
static study() {
console.log('i study')
}
}
5.如何继承一个类
在es5中的继承:
// 定义一个父类
let Da = function(type) {
this.type = type
}
// 定义方法
Da.prototype.eat = function() {
console.log('i am')
}
// 定义静态方法
Da.study = function(book) {
console.log('i study')
}
// 定义子类
let Da1 = function() {
// 初始化父类
Da.call(this, 'da1');
this.run = function() {
console.log('i run')
}
}
// 继承
Da1.prototype = Da.prototype
在es6中的继承
class Da {
constructor(type) {
this.type = type
}
eat() {
// Da.walk();
console.log('i eat')
}
static walk(){
console.log('i walk')
}
}
class da extends Da {
// 构造函数
//constructor (type) {
//super(type)
//}
run() {
console.log('i run')
}
}
let da1 = new da('da1')
6.面向对象编程Class
类的声明,属性,方法,静态方法,继承,多态,私有属性
// 类的声明
let Da = function(type) {
this.type = type
this.eat = function() {
console.log('i eat');
}
}
let da = new Da('da');
// prototype
let Da = function(type) {
this.type = type
}
Da.prototype.eat = function() {
console.log('i eat')
}
let da1 = new Da('da1')
es6中的Class
class Da {
// 构造函数
constructor(type) {
this.type = type
}
// 方法
walk() {
console.log('i walk')
}
}
let da = new Da('da');
// console.log(typeof Da); function
7.函数参数的默认值
函数参数是从左到右解析,如果没有默认值会被解析成
undefined
// 参数默认值
function da (x,y,z) {
}
function sum() {
let num = 0
Array.prototype.forEach.call(arguments, function(item){
num += item * 1
})
Array.from(arguments).forEach(function(item){
num += item * 1
})
return num
}
// 不确定
function sum(...nums) {
let num = 0
nums.forEach(function(item){
num += item * 1
})
return num
}
console.log(sum(1,2,3,4,5))
function sum () {
let num = 0
Array.prototype.forEach.call(arguments, function (item) {
num += item * 1
})
return num
}
function sum (...nums) {
let num = 0
nums.forEach(function (item) {
num += item * 1
})
return num
}
8.es6箭头函数
箭头函数表达式的语法比函数表达式更简洁,并且没有自己的this,arguments,super或new.target。箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数。
() => {}
// function Da() {}
// let da = function() {}
let da = () => {
console.log('hello')
}
da()
let da = name => {}
const materials = [
'Hydrogen',
'Helium',
'Lithium',
'Beryllium'
];
console.log(materials.map(material => material.length));
// expected output: Array [8, 6, 7, 9]
拓展
判断函数有几个参数
- 在 ES5 中可以在函数体内使用 arguments 来判断。
- 在 ES6 中可以借助 Function.length 来判断。(统计第一个默认参数前面的变量数)
console.log(sum(...[4]))
console.log(sum.apply(null, [4]))
9.JavaScript中的三个点(…)
JavaScript当中,函数的参数前面有三个点,代表什么呢?我们看下代码示例:
function myFunc(a, b, ...args) {
console.log(a); // 22
console.log(b); // 98
console.log(args); // [43, 3, 26]
};
myFunc(22, 98, 43, 3, 26);
function myFunc(x, y, ...params) { // used rest operator here
console.log(x);
console.log(y);
console.log(params);
}
var inputs = ["a", "b", "c", "d", "e", "f"];
myFunc(...inputs); // used spread operator here
// "a"
// "b"
// ["c", "d", "e", "f"]
var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };
var clonedObj = { ...obj1 };
// Object { foo: "bar", x: 42 }
var mergedObj = { ...obj1, ...obj2 };
// Object { foo: "baz", x: 42, y: 13 }
10.Object Property
JS中对象的属性定义,代码示例如下:
let x = 'da1';
let y = 'da2';
let obj = {
x,
y
}
console.log(obj);
// 结果
{x:'da1',y:'da2'}
let x=1; let y=2; let z=3
let obj = {
'x': x,
y,
[z+y]: 4,
* hello() { // 异步
console.log('dada')
}
}
// function* functionName() {}
obj.hello()
11.Set数据结构
Set存储的成员不允许的重复的(它类似于数组)
Set 本身是一个构造函数,用来生成 Set 数据结构。
const s = new Set();
[2, 3, 5].forEach(x => s.add(x));
Set 函数可以接受一个数组(或类似数组的对象)作为参数,用来初始化
const set = new Set([1, 2, 3, 4, 4]);
实现数组去重
var arr = [1,1,2,2,3,3]; // step1:数组转集合
var s = new Set(arr); // 已经去掉重复值,当前不是数组,而集合
s.size; // 3
// step2:集合转数组
console.log([...s]); // 1,2,3;
// Array.form 方法可以将 Set 结构转为数组
const items = new Set([1, 2, 3]);
const arr = Array.from(items);
function dada(array) {
return Array.from(new Set(array));
}
dada([1, 1, 2])
Set的遍历
-
keys()
:返回键名
的遍历器 -
values()
:返回键值
的遍历器 -
entries()
:返回键值对
的遍历器 -
forEach()
:使用回调函数遍历每个成员
操作方法
-
add(value)
:添加某个值
,返回Set结构本身。 -
delete(value)
:删除某个值
,返回一个布尔值,表示删除是否成功。 -
has(value)
:返回一个布尔值
,表示该值是否为Set的成员。 -
clear()
:清除所有成员
,没有返回值。
let set = new Set([1, 2, 3, 4, 4]);
// 添加数据
let addSet = set.add(5);
console.log(addSet); // Set(5) {1, 2, 3, 4, 5}
// 删除数据
let delSet = set.delete(4);
console.log(delSet); // true
// 查看是否存在数据 4
let hasSet = set.has(4);
console.log(hasSet); // false
// 清除所有数据
set.clear();
console.log(set); // Set(0) {}
实现并集(Union)、交集(Intersect)和差集(Difference)
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2, 1]);
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {1, 2, 3}
// 差集
let difference = new Set([...b].filter(x => !a.has(x)));
// Set {4}
12.Map数据结构
JS当中的哈希表,使用方法如下:
let map = new Map()
map.set(1, 2)
map.set(3, 4)
map.set(1, 3)
console.log(map)
创建
var da = new Map();
var jeskson = {};
遍历
da.forEach(function(value,key,map){}
长度
da.size
删除
//da.delete() 删除key,全部清楚da.clear()
新增
da.set(key,value)
da.has(查索引值)
da.forEach((value,key) =>{
})
for( let [key, value] of map){}
// let map = new Map( [[1,2], [3,4]] )
map的key任意都可以
let o = function() {
console.log('o')
}
map.set(o, 3)
console.log(map.get(o)); // 3
// map.js
var Dictionary = function() {
var items = {};
// 检查键
this.has = function(key) {
return key in items;
}
// 添加键值对
this.set = function(key, value){
items[key] = value;
}
// 通过键移除元素
this.delete = function(key) {
if(this.has(key)){
delete items[key]
return true
}
return false
}
// 键获取值
this.get = function(key){
return this.has(key) ? items[key] : undefined;
}
// 列表返回字典值
this.values = function() {
var values = [];
for(var k in items) {
if(this.has(k)) {
values.push(items[k])
}
}
return values;
}
// 获取全部键名
this.keys = function() {
return Object.keys(items);
}
// 获取私有变量items
this.getItems = function() {
return items;
}
}
Map数据结构,它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
13.Object.assign(对象的拷贝)
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }
> Object { a: 1, b: 4, c: 5 }
> Object { a: 1, b: 4, c: 5 }
语法
Object.assign(target, ...sources)
参数
target
目标对象
sources
源对象
返回值
目标对象。
const obj = { a: 1 };
const copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
- Object.assign()拷贝的是(可枚举)属性值
- Object.assign方法的第一个参数是目标对象,后面的参数都是源对象
- 如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性
- 由于undefined和null无法转成对象,所以如果它们作为参数,就会报错
- 如果undefined和null不在首参数,就不会报错
- 如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用(这个对象的任何变化,都会反映到目标对象上面。)
Object.assign(undefined) // 报错
Object.assign(null) // 报错
let obj = {a: 1};
Object.assign(obj, undefined) === obj // true
Object.assign(obj, null) === obj // true
const obj1 = {a: {b: 1}};
const obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
obj2.a.b // 2
const target = { a: { b: 'c', d: 'e' } }
const source = { a: { b: 'hello' } }
Object.assign(target, source)
// { a: { b: 'hello' } }
const source = {
get foo() { return 1 }
};
const target = {};
Object.assign(target, source)
// { foo: 1 }
Object.assign复制的是属性值value,如果属性值是一个引用类型,那么复制的其实是引用地址,就会存在引用共享的问题(Object.assign(target,source1,...,sourceN)浅拷贝的过程)
要点:
function ObjectAssign(target, ...sources) {
// 对第一个参数的判断,不能为undefined和null
if(target === undefined || target === null) {
throw my TypeError('error');
}
// 将第一个参数转为对象(不是对象转换为对象)
const targetObj = Object(target);
// 将源对象自身的所有可枚举属性复制到目标对象
for(let i = 0; i<sources.length; i++){
let source = sources[i];
// 对于undefined和null在源中不会报错,会直接跳过
if(source !== undefined && source !== null) {
// 将源象转换成对象
// 需要将源自身的可枚举数据进行复制
// Reflect.ownKeys(obj)
const keysArray = Reflect.ownKeys(Object(source));
for (let nextIndex = 0; nextIndex < keysArray.length; nextIndex++) {
const nextKey = keysArray[nextIndex];
// 去除不可枚举属性
const desc = Object.getOwnPropertyDescriptor(source,nextKey);
if(desc!==undefined&&desc.enumerable){
targetObj[nextKey] = source[nextKey];
}
}
}
}
return targetObj;
}
if(typeof Object.myAssign !== 'function'){
Object.defineProperty(Object, 'myAssign', {
value: ObjectAssign,
writable: true,
enumerable: false,
configurable: true
});
}
浅拷贝 Object.assign 的实现原理
拷贝第一层的基本类似值和第一层的引用类型地址:
let da1 = {
name: 'da1',
age: 1
}
let da2 = {
name: 'da2',
study: {
title: 'web'
}
}
let da3 = Object.assign(da1,da2);
console.log(da3);
// {
// name: 'da2',
// age: 1,
// study: { title: 'web' }
// }
console.log( da1 === da3); // true
da2.name = 'da22';
da2.study.title = 'web2';
console.log(da2);
// {
// name: 'da22',
// study: { title: 'web2' }
// }
console.log(da1);
// {
// age: 1,
// name: 'da2',
// study: { title: 'web2' }
// }
如果源对象的属性值是一个指向对象的引用,它也只拷贝这个引用地址哦!
let da1 = {
name: 'da1',
age: 1
}
let da2 = {
a: Symbol('dadaqianduan'),
b: null,
c: undefined
}
let da3 = Object.assign(da1, da2);
console.log(da3);
// {
// name: 'da1',
// age: 1,
// a: Symbol('dadaqianduan'),
// b: null,
// c: undefined
// }
console.log(da1 === da3); // true
let map = new Map([iterable])
// Map是用来实现字典的功能-Object键值对
动态属性键
// ES5 code
var
key1 = 'one',
obj = {
two: 2,
three: 3
};
obj[key1] = 1;
// obj.one = 1, obj.two = 2, obj.three = 3
// ES6 code
const
key1 = 'one',
obj = {
[key1]: 1,
two: 2,
three: 3
};
// obj.one = 1, obj.two = 2, obj.three = 3
// ES6 code
const
i = 1,
obj = {
['i' + i]: i
};
console.log(obj.i1); // 1
补充:前端面试考点,HTML和CSS,性能优化,原型,作用域,异步,各种手写代码,DOM事件和Ajax,HTTP协议。
- css(布局,定位,移动端响应式)
- es(原型,原型链,作用域,闭包,异步,单线程)
- webapi(DOM BOM,Ajax跨域,事件存储)
- 开发环境(版本管理,调试抓包,打包构建)
- 运行环境(页面渲染,性能优化,web安全)
- 网络通讯
- 布局(盒模型,BFC,float,flex)
- 定位,图文样式,移动端响应式(rem,media query,vw/vh),动画、渐变
- 变量类型和计算(值类型和引用类型,类型判断,逻辑运算)
- 原型和原型链(class,继承,原型,原型链,instanceof)
- 作用域和闭包(作用域,*变量,闭包,this)
- 异步(单线程,callback,应用场景,Promise,event-loop,async/await,微任务/宏任务)
- 模块化(ES6 Module)
- DOM(树形结构,节点操作,属性,树结构操作,性能)
- BOM(navigator,screen,location,history)
- 事件(绑定,冒泡,代理)
- ajax(XMLHttpRequest,状态码,跨域)
- 存储(cookie,localStorage,sessionStorage)
- 开发环境(git,调试,webpack和babel,linux命令)
- 运行环境(页面加载:加载,渲染。性能优化:加载资源优化,渲染优化。安全:xss,CSRF)
- HTTP协议:状态码,Method,Restful API,headers,缓存策略
14.模板文字
模板文字是es2015/es6的新功能,与es5及以下版本相比,可以通过新颖的方式使用字符串,先只需要反引号代替单引号或双引号即可:
const module_string = `dadaqianduan`
它们之所以独特是因为它们提供了很多用引号构建的普通字符串不具备的功能:
- 提供了定义多行字符串的语法;
- 提供了一种简单的方法来插值字符串中的变量和表达式
- 允许您使用模板标签创建DSL(领域特定的语言)
使用多行字符串
在es6之前的版本:
// 要创建跨越两行的字符串,必须\在行尾使用字符
const dada =
'dada \
dadaqianduan'
// 呈现效果:在两行上创建一个字符串,但是仅在一行上呈现
要在多行上呈现,则需要使用\n
在每行的末尾添加
const string =
'dada 魔王哪吒\n \
dadaqianduan'
使用反引号打开模板文字后,只需要按enter键就行:
const dada = `dadaqianduan
魔王哪吒`
在这里请记住空间是有意义的:
const da = `First
Second`
使用trim()方法,可以消除第一个字符之前的任何空格
插补:模板文字提供了一种将变量和表达式插入字符串的简便的方法
const da = `dadaqianduan ${mydada}`
${}里面可以添加任何东西
const da1 = `dada ${1+2+3}`
const da2 = `dada ${dafun() ? 'x' : 'y'}`
15.什么是解构赋值
let da = ['hello', 'world']
let [firstName, surName] = da
cosole.log(firstName, surName);
解构赋值在于赋值,拷贝出来赋值给变量,而赋值的元素本身不会发生改变
默认值
let [da1, da2] = [];
console.log(da1); // undefined
console.log(da2); // undefined
给变量赋值(默认值),防止出现undefined的情况:
let [da1= 'da1', da2 = 'da2']=['dadaqianduan]
console.log