JavaScript知识点

事件委托

利用冒泡的原理,把事件加到父级上,触发执行效果
event.target target.nodeName来判断是哪个标签

优点:

1、减少事件注册,节省内存。比如,

在table上代理所有td的click事件。
在ul上代理所有li的click事件。
2、简化了dom节点更新时,相应事件的更新。比如

不用在新添加的li上绑定click事件。
当删除某个li时,不用移解绑上面的click事件。

缺点:
1、事件委托基于冒泡,对于不冒泡的事件不支持。
2、层级过多,冒泡过程中,可能会被某层阻止掉。
3、理论上委托会导致浏览器频繁调用处理函数,虽然很可能不需要处理。所以建议就近委托,比如在table上代理td,而不是在document上代理td。
4、把所有事件都用代理就可能会出现事件误判。比如,在document中代理了所有button的click事件,另外的人在引用改js时,可能不知道,造成单击button触发了两个click事件。

//例子
<body>
    <div id="div1"></div>
</body>

window.onload = function(){
     
    let body = document.querySelector('body');
    let div1 = document.getElementById('div1');
    body.addEventListener('click',function(){
     
        console.log('打印body')
    })
    div1.addEventListener('click',function(){
     
        console.log('打印div1')
    })
}

文档碎片

document.createDocumentFragment() 一个容器,用于暂时存放创建的dom元素


原型模式的优缺点:

1、优点:可以让所有的对象实例共享它所包含的属性和方法

2、缺点:原型中是所有属性都是共享的,但是实例一般都是要有自己的单独属性的。所以一般很少单独使用原型模式。

prtotype、proto、constructor
◆每一个构造函数,都有一个原型[[prototype]]属性 指向构造函数的原型对象
◆每一实例化对象都有一个隐式原型_proto_ 指向构造函数的原型对象
◆每一个原型对象都有一个默认的constructor属性,指向对象的构造函数


闭包

指的是:能够访问另一个函数作用域的变量的函数
《JavaScript高级编程》书中建议:由于闭包会携带包含它的函数的作用域,因为会比其他函数占用更多内容,过度使用闭包,会导致内存占用过多。

//例如:
function init() {
     
    var name = "Mozilla"; // name 是一个被 init 创建的局部变量
    function displayName() {
      // displayName() 是内部函数,一个闭包
        alert(name); // 使用了父函数中声明的变量
    }
    displayName();
}
init();

js链式作用域(冒泡同理):
子对象会一级一级向上寻找所有父对象的变量,反之不行。

js变量两种作用域:
全局变量、局部变量(函数内):js中函数内部可以读取全局变量,函数外部不能读取函数内部的局部变量。

闭包为什么可以实现在函数外读取到函数内的变量:
原因:init是displayName的父函数,displayName被赋给了一个全局变量,displayName始终存在内存中,displayName的存在依赖init因此init也始终存在内存中,不会在调用结束后,被垃圾回收机制回收。


class es6

class a{
     name = 'aaa'}
function b(){
     this.name = 'aaa'}
JSON.stringify(new ab()) === JSON.stringify(new abc())  ===  "{name = 'aaa'}"

一、运算符和关键字:
1、new
创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。
简单理解就是创建一个对象,把this指向的各个数据放进来。

语法:
new constructor[([arguments])]

//constructor	一个指定对象实例的类型的类或函数。 Object.prototype.constructor
	//arguments	一个用于被 constructor 调用的参数列表。
	示例:
	//设置类A,这里可以是常规函数 function A(){}
	class A {
     }
	// 设置A的size属性
	A.size = 11111
	// 设置实例化后的size属性
	new A().__proto__.size = 22222
	//打印一下结果对比
	A.size // 11111
	new A().__proto__.size  // 22222
注:如果你没有使用 new 运算符, 构造函数会像其他的常规函数一样被调用, 并不会创建一个对象。在这种情况下, this 的指向也是不一样的。

2、super:

super()===A.prototype.constructor.call(this)

super可以造访继承的父级原型对象、可访问父级的原型方法(针对static静态方法调用)

3、static对象:
static修饰的方法–可以直接调用或者new一个对象调用,
只能用new的方法调用,不可直接用 类.方法名 调用

//例如
class A{
     
    b = 2;
    static c = 3;
}
new A().b  //2
new A().c // undefined
A.b          //undefined
A.c  	//3

array数组的增删改查排序

增加:

push() 方法可向数组的末尾添加一个或多个元素
unshift()  方法是向数组头部添加一个或多个元素,使用上同push方法
concat() 连接两个或以上的数组,返回的是一个新数组,不影响原数组

删除:

pop() 方法删除数组最后一个元素,该方法会改变原数组,删除元素并且数组长度-1,返回值是删除的元素的值,如果数组是空数组,则不进行删除,返回值是undefined 
shift() 方法删除数组第一个元素,使用上同pop方法

修改:

slice(start,end) 数组的截取函数,start 必需,end 选填 ,均可以为负数,返回的是start到end(不包括end)之间的元素,返回新数组,不影响原来数组 (slice 切开)
splice(index,howmany,item1,.....,itemX) 方法删除或者添加数组,会操作原数组,返回的是含有被删除的元素的数组,index 必需 起始位置,howmany 必需 数量可以为0,即不操作,第三个参数添加到数组 替代数组index的位置

排序:

 sort() 对数组进行排序,改变的是原数组元素顺序,默认是按字符编码排序,所以在遇到数字排序时,就需要定义函数
reverse() 颠倒元素顺序,改变原数组

其他:

indexOf() 找出某个元素在数组中的索引,并返回该元素索引。
join() 方法将数组拆分成字符串,返回值字符串,默认分隔符为逗号“,”
toString() 将数组转换成字符串,返回字符串,格式为逗号隔开

Object属性方法

var obj = {
      0: "a", 1: "b", 2: "c"}; // 以下示例以此对象为例
//属性:

Object.prototype.writable//:默认为false 
Object.prototype.enumerable//:默认为false 
Object.prototype.configurable//:默认为false 
Object.prototype.constructor//:用于创建一个对象的原型。 

//常用方法:
Object.prototype.hasOwnProperty()//:返回一个布尔值,表示某个对象是否含有指定的属性,而且此属性非原型链继承。

Object.prototype.isPrototypeOf()//:返回一个布尔值,表示指定的对象是否在本对象的原型链中。

Object.prototype.propertyIsEnumerable()//:判断指定属性是否可枚举。

Object.prototype.toString()//:返回对象的字符串表示。

Object.prototype.watch()//:给对象的某个属性增加监听。

Object.prototype.unwatch()//:移除对象某个属性的监听。

Object.prototype.valueOf()//:返回指定对象的原始值。

Object.create(proto,[propertiesobject])//:创建一个拥有指定原型和若干个指定属性的对象。

Object.keys(obj) = Object.getOwnPropertyNames(obj).sort()//:返回一个由给定对象的所有可枚举自身属性的属性名组成的数组
	//举例:
	console.log(Object.getOwnPropertyNames(obj).sort()); //["0", "1", "2"]
	Object.keys(obj)//["0", "1", "2"]

Object.values(obj)//:返回一个包含指定对象所有的可枚举属性值的数组,数组中的值顺序和使用for…in循环遍历的顺序一样。
	//举例:
	Object.values(obj);//["a", "b", "c"]

Object.entries(obj)//:返回一个包含由给定对象所有可枚举属性的属性名和属性值组成的 [属性名,属性值] 键值对的数组,数组中键值对的排列顺序和使用for…in循环遍历该对象时返回的顺序一致。 
	//举例: 
	console.log(Object.entries(obj)); // [["0","a"],["1","b"],["2","c"]]

Object.assign(target, …sources)//:参考数组操作方法concat(),把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。
	//举例:
	Object.assign(obj,{
     3:'d'},{
     4:'e',5:'f'});//{0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f"}

	

//不常用的方法:

Object.getPrototypeOf(object)//:返回该对象的原型。

Object.is(value1, value2)//:判断两个值是否是同一个值。

Object.isExtensible(obj)//:判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。

Object.isFrozen(obj)//:判断一个对象是否被冻结(frozen)。

Object.isSealed(obj)//:判断一个对象是否是密封的(sealed)。密封对象是指那些不可 扩展 的,且所有自身属性都不可配置的(non-configurable)且属性不可删除的对象(其可以是可写的)。

Object.freeze(obj)//:冻结一个对象,冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。

Object.getOwnPropertyDescriptor(obj, prop)//:返回指定对象上一个自有属性对应的属性描述符。

Object.defineProperties(obj, props)//:在一个对象上添加或修改一个或者多个自有属性,并返回该对象。

Object.defineProperty(obj, prop, descriptor)//:直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象。obj:需要定义属性的对象。prop:需定义或修改的属性的名字。descriptor:将被定义或修改的属性的描述符。


Set 和 Map es6

相同点:Set和Map都可以用来生成新的 Map
例:

 var  a = [['foo', 1],['bar', 2]];
	new Map(new Set(a)) === new Map(a) === Map(2) {
     "foo" => 1, "bar" => 2}
// 当然上例虽然值是相等的但是实际并不相等,这里只是看结果,因为Map 的键实际上是跟内存地址绑定

不同点:
1、Set适合操作数组,Map适合操作对象
2、Map具有set(key, value)和get(key)方法操作数据 Set使用add(value)方法

set 不重复的值的集合 es6

使用:new set()

//数组去重:
1[...new Set(array)]
2、Array.from(new Set(array))
//字符串去重
[...new Set(string)].join('')

遍历:

方法 同Object  :因为Set方法内部为对象形式。
new Set().add('a').add('b').forEach(data=>{
     console.log(data)}) //a //b
方法 同array    :具有数组的特性  
for(let data of new Set().add('a').add('b').keys()){
     console.log(data)} //a //b

map

同Set


Echarts

实现方式 纯 js 实现,MVC 封装,利用canvas绘制图形
特点:重要性和优先级依次递减,设计效果直观、生动,能够交互,可个性化定制。

MVC 架构:
Storage(M):模型层,实现图形数据的CURD(增删改查)管理;
Painter(V): 视图层,实现canvas 元素的生命周期管理,即:视图渲染、更新控制、绘图;
Handler©:控制层,事件交互处理,实现完整的dom事件模拟封装。


Proxy es6

代理拦截

//target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
var proxy = new Proxy(target, handler);

js内存分配(垃圾回收机制)

内存生命周期
不管什么程序语言,内存生命周期基本是一致的:

1、分配你所需要的内存
2、使用分配到的内存(读、写)
3、不需要时将其释放\归还 (对象有没有其他对象引用到它)

垃圾回收

  1. 引用:如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收 标记-清除算法 从根(全局对象)开始
  2. 将可能被引用的对象用递归的方式进行标记,然后将没有标记到的对象作为垃圾进行回收。

Promise构造函数 es6

语法:

new Promise( function(resolve, reject) {
     ...} /* executor */  );

方法

Promise.all()  
Promise.allSettled()   
Promise.any()  
Promise.prototype.catch()
Promise.prototype.finally()
Promise.prototype.then()
Promise.race()
Promise.reject()
Promise.resolve()

有以下几种状态:
pending: 初始状态,既不是成功,也不是失败状态。
fulfilled: 意味着操作成功完成。
rejected: 意味着操作失败。

Promise + axios +qs 同步操作

关键字说明:
async:放在函数声明之前,使其成为 async function
await:它会暂停代码在该行上,直到 promise 完成

//模拟ajax异步操作1
   function ajax1() {
     
       const p = new Promise((resolve, reject) => {
     
           this.$axios({
     
	          url: '',
	          method: 'POST',
	          data: this.qs.stringify({
     a:1})
	        }).then(function (res) {
     
	          resolve(res.data)
	        })
   })
       return p
 
   }
       //模拟ajax异步操作2
   function ajax2() {
     
       const p = new Promise((resolve, reject) => {
     
           this.$axios({
     
	         url: '',
	          method: 'POST',
	          data: this.qs.stringify({
     a:1})
	        }).then(function (res) {
     
	          resolve(res.data)
	        })
   })
       return p
   }
   //等待两个ajax异步操作执行完了后执行的方法
  async function myFunction() {
     
       const x = await ajax1()
       console.log(x)
       const y = await ajax2()
       //等待两个异步ajax请求同时执行完毕后打印出数据
       console.log(x, y)
   }

Date日期时间戳获取转换

 //获取一个时间对象 可以添加时间或者时间戳 例如new Date(1594224000000)
var  time = new Date()   ;    
/*
*方法:
*/
var year = time.getFullYear() // 获取年
var month = time.getMonth(); // 获取月 注:月份从0开始计数 所以这里需要+1得到实际月份
var hour = time.getHours();  // 获取小时
var minute = time.getMinutes(); // 获取分钟
var second = time.getSeconds(); // 获取秒

/*
*将日期格式转换成时间戳:
*/
var time1 = date.getTime();
var time2 = date.valueOf();
var time3 = Date.parse(date);

//三种获取的区别:
time1 、time2:会精确到毫秒,其实这两种方法是有区别的在万为单位的获取中会有差异
time3:只能精确到秒,毫秒将用0来代替

/*
*date的几种参数形式 :
*月份从0~11
*/
// 
new date("month dd,yyyy hh:mm:ss"); 
new date("month dd,yyyy"); 
new date("yyyy/mm/dd hh:mm:ss"); 
new date("yyyy/mm/dd"); 
new date(yyyy,mth,dd,hh,mm,ss); 
new date(yyyy,mth,dd); 
new date(ms); // (1594224000000)

注:经常我们的日期格式会用“-”链接,也就是“2018-08-08”这种形式,放入以下方法有可能不会报错,但是手机浏览器会报错,避免不必要的麻烦,尽量转换成以下任意一种格式使用。


模块化开发

模块化是一种将系统分离成独立功能部分的方法,可将系统分割成独立的功能部分,严格定义模块接口、模块间具有透明性。

优点:
1、代码重用时,引入js文件的数目可能少了,避免来代码的累赘。
2、代码复用高,开发效率也会提高。
3、方便后期的维护

缺点:
1.系统分层,调用链会很长
2.模块间通信,模块间发送消息会很耗性能

常见的模块化的写法

1、原始写法(封装函数)

这种做法的缺点很明显:"污染"了全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接关系。

2、对象写法

这样的写法会暴露所有模块成员,内部状态可以被外部改写

3、立即执行函数写法

立即执行函数可以达到不暴露私有成员的目的


解构赋值:es6

重点:
1、等号左边为var 的变量,右边为值得数组,两边通过key值一一赋值。
2、如果左边有属性赋值,就赋值右侧的对象属性。
3、只要等号右边的值不是对象或数组,就先将其转为对象 ,所以undefined和null 无法转换,会报错

简单了解解构赋值(数组的对象都有一个length属性):
以下例子统一变量 :

const 	str = '123', 
		value = [4, 5, 6],
		obj={
     a:7,b:8,c:9};

以下例子统一打印方法:

console.log('a='+a,'b='+b,'c='+c);

1、字符串: 变量 = 值

let [a,b,c]  = str;  
// 解析过程var [a,b,c][0]=str[0],[a,b,c][1]=key[1],[a,b,c][2]=[a,b,c][2];  
//结果var a=1,b=2,c=3;

2、数组:数组[0] = 数组[0]

let [a,b,c] = value  // 解析过程如上,结果为:var a=4,b=5,c=6;

3、对象:

let {
     a, b, c}  = obj;//解析过程如上,结果为:var a=7,b=8,c=9;

4、函数:这里涉及到解构赋值以及默认值方式,注意函数的结构赋值跟以上三种赋值方式一样,这里提供一个例子:

function add({
     x, y,z = 0}){
     
    			return x + y +z;
		}
		console.log(add({
     x : 1, y :2}))//3

5、属性赋值(以上讲的都是变量赋值,如果给变量添加属性,那么就是属性赋值):

let {
      valueOf: a } = obj;  // ƒ valueOf() { [native code] }

说明:上诉valueOf设置之后解析的就是obj的valueOf属性 如果设置其他属性同理。
这里打印出来a的值为 obj.prototype.valueOf

默认值: 所有定义的左侧变量都可以设置默认值 使用方式 ‘key=value’赋值,作用于右侧没有相关赋值操作后,保留默认值,否则更新值。
例如:

 let [a = 44,b,c,d = 77] = value  
 // 解析过程如上,
 //结果为:var a=4,b=5,c=6,d=77;

实际应用:

const {
     merge} = require('webpack-merge');  
//新版webpack的功能模块webpack-merge引入等

说明:引入方式可能有的不需要用解构赋值,应为版本原因,不支持es6语法导致。


js值和引用

1、概念

引用类型的保存与复制的是指向对象的一个指针(指向内存地址),同时同步改变原来的变量。

例一:
var a = 1;
var b = a;
b = 2;
a===b;//false  因为a=1,b=2

例二:
var a = {
     x:1};
var b = a;
b.x = 2;
a===b;//true  因为a ={x:2},b={x:2} //因为a和b指向的引用内存地址相同

例三:
var a = {
     x:1};
var b = {
     x:1};
a===b;//false  因为a和b指向的引用内存地址不同

基本类型

• 字符串(string)
• 数字(number)
• 布尔值( boolean)
• 空值(null)
• 未定义(undefined)
• 符号(symbol)

引用类型

• 对象( Object、Array、RegExp、Date、Function)

2、符号说明

“=”:普通类型为赋值或拷贝,对象为引用
“===”:普通类型为对比值,引用类型对比的是内存地址,如上例二、例三。

3、注意点

这里注意:如果引用类型重新赋值也会造成内存地址重新定义
如上面例二,咱们改装一下:

var a = {
     x:1};
var b = a;
b = {
     x:1}; // 重新赋值,改变了内存地址
a===b;//false   因为a和b指向的引用内存地址不同

4、形参属性的变更示例

var a = {
     x : 1}; 
function f(o) {
     
    //o = 100;  // 仍然是1, obj并未被修改为100.
    o.x = 100   // 100,obj已经被修改
} 
f(a); 
console.log(a.x); 

说明:

  • 形参也可以对指针对象操作
  • 如果重新赋值也会新建内存地址,不影响原数据

如果一个变量绑定到一个非基本数据类型,那么它只记录了一个内存地址,该地址存放了具体的数据。
指向基本数据类型的变量相当于包含了数据,而现在指向非基本数据类型的变量本身是不包含数据的。

深拷贝

ps:上面提到引用的概念引申:浅拷贝和深拷贝
场景:你想利用之前的列表Object数据,但是又不想更改之前的Object数据,那么这里就涉及到浅拷贝和深拷贝了。
理解:所谓的浅拷贝就是直接引用之前数据的指针地址,缺点是你更改内部数据会影响之前数据的正常使用,这里就引申到深拷贝:所谓的深拷贝就是重新做一个内存地址,然后将原来的Object对象递归的形式赋值进新数组。

//深拷贝简单例子:
A: var AO ={
     'a':1,'b':2};
形成深拷贝数组B: 
1、设置内存空间
var BO = {
     }
2、赋值
for(i in AO){
     
  BO[i] =AO[i]
}
//然后你就可以肆无忌惮的使用或更改BO的对象不用担心AO变形了

知识点更新到此告一段落。
前段时间持续内容和修改点我并没有发出来,但不影响之前的发布,本章所有知识点都是本人通过查阅大量资料总结归纳,以我个人感觉最优的方式呈现出来的,有问题希望帮忙指点提出,在此感谢。
本章知识点只是我感觉应该了解或者掌握的基础,如需深入了解可自行学习。

你可能感兴趣的:(javascript)