JS小总结

1.js和jQuery中的数组迭代方法 数组常用方法

原生的迭代方法

arr.every(function):如果数组中的每个元素都能满足回调函数的判断,返回true,否则false,回调函数的返回值必须式布尔值
arr.filter(function):数组中满足回调函数的元素构成新数组,filter方法返回这个新数组,回调函数的返回值必须式布尔值
arr.forEach(function):数组中的每个元素执行一次回调函数,返回undefined
arr.map(function):数组中的元素通过回调函数映射成一个新的值,这些回调函数的返回值构成新数组,map方法返回这个新数组
arr.some(function):数组中一个元素以上满足回调函数,返回true,回调函数的返回值为布尔值

jQuery的迭代

jQuery隐藏了隐式迭代
显式迭代的方法式each(function)

数组常用的方法

slice(): 参数1:从第几位开始截取,参数2:截取到第几位,不包含这一位,不填表示截取到最后 返回截取的数组,原数组不变
splice(): 参数1: 从第几位开始截取,参数2:截取的长度,不填表示截取到最后 参数3: 用来替换的元素,返回删除的数组,原数组改变

2.jquery获取屏幕宽度的方法总结

width()方法: 只能获取内容宽度
innerWidth()方法:  获取内容宽度+padding
outerWidth()方法: 获取内容宽度+padding+border
outerWidth(true)方法:获取内容宽度+padding+border+margin

3.js和jQuery添加元素的方法

js添加元素的方法

element.cloneNode(true): 克隆一个元素
parent.appendChild(element.cloneNode(true)):在父元素的最后新增一个子元素,这个元素是拷贝来的,而不是移动来的
parent.insertBefore(element.cloneNode(true)):在父元素的最前面新增一个子元素,这个元素是拷贝来的,而不是移动来的

jQuery添加元素的方法

$('element').clone()
$('parent').append($('element').clone()):在父元素的最后新增一个子元素,这个元素是拷贝来的,而不是移动来的
$('parent').prepend($('element').clone()):在父元素的最前面新增一个子元素,这个元素是拷贝来的,而不是移动来的

4.js的节点属性和jQuery的节点方法

js的节点属性

parentNode:父节点
childNodes:一级子节点
children:一级子元素
nextSibling/previousSibling:兄弟节点
nextElementSibling/previousElementSibling:兄弟元素
firstChild/lastChild:第一个或最后一个子节点
firstElementChild/lastElementChild:第一个或最后一个子元素

jQuery的节点方法

parent():父元素
contents():一级子节点
children():一级子元素
find():所有后代元素
next()/previous():下一个或前一个兄弟元素
siblings():所有的兄弟元素
nextAll():后面所有兄弟元素
previousAll():前面所有兄弟元素

5.html文档的解析顺序

  • 下载完整的html文件
  • 自上而下解析html文档
  • 遇到link标签,下载相应文本,下载的同时,继续解析html,下载完成后,解析css文件构建CSSOM,这个过程中,html文档继续在解析
  • 遇到script标签,暂停html文档解析,将控制权交给JS引擎,下载js脚本并解析执行,完毕后将控制权交还给渲染引擎,继续解析html
  • DOM树和CSSOM构建完毕后,结合生成render树,即渲染树
  • 根据渲染树布局,计算元素尺寸与位置
  • 根据渲染树绘制,页面展示出来

6.页面的生命周期

  • DOMContentLoaded事件在DOM树构建完毕后被触发,我们可以在这个阶段使用js去访问元素。
    async和defer的脚本可能还没有执行。
    图片及其他资源文件可能还在下载中。
  • load事件在页面所有资源被加载完毕后触发,通常我们不会用到这个事件,因为我们不需要等那么久。
  • beforeunload在用户即将离开页面时触发,它返回一个字符串,浏览器会向用户展示并询问这个字符串以确定是否离开。
  • unload在用户已经离开时触发,我们在这个阶段仅可以做一些没有延迟的操作,由于种种限制,很少被使用。
  • document.readyState表征页面的加载状态,可以在readystatechange中追踪页面的变化状态:
    loading — 页面正在加载中。
    interactive – 页面解析完毕,时间上和 DOMContentLoaded同时发生,不过顺序在它之前。
    complete – 页面上的资源都已加载完毕,时间上和window.onload同时发生,不过顺序在他之前。

7.浏览器的组成

浏览器包含用户界面,浏览器引擎,渲染引擎,网络模块,UI后台(绘制),dataStorage(管理用户数据)
其中最重要的渲染引擎(内核)又分为以下几个线程:

  • GUI 渲染线程:
    HTML Parser 解析HTML文档,将元素转换为树结构DOM节点,称之为Content Tree
     CSS Parser 解析Style数据,包括外部的CSS文件以及在HTML元素中的样式,用于创建另一棵树,调用“Render Tree”
     Layout过程 为每个节点计算出在屏幕中展示的准确坐标
     Painting 遍历Render Tree,调用UI Backend提供的接口绘制每个节点
  • JavaScript 引擎线程:处理js代码
  • 浏览器定时触发器线程
    浏览器定时计数器并不是由 JavaScript 引擎计数的, 因为 JavaScript 引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确, 因此通过单独线程来计时并触发定时是更为合理的方案
  • 浏览器事件触发线程
    当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待 JavaScript 引擎的处理。这些事件可以是当前执行的代码块如定时任务、也可来自浏览器内核的其他线程如鼠标点击、AJAX 异步请求等,但由于 JavaScript 的单线程关系所有这些事件都得排队等待 JavaScript 引擎处理。
  • 浏览器 http 异步请求线程
    在 XMLHttpRequest 在连接后是通过浏览器新开一个线程请求, 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到 JavaScript 引擎的处理队列中等待处理。

9.js获取元素到屏幕距离的方法getBoundingClientRect()

const ballPosition = this.$refs.ball.getBoundingClientRect();
//计算徽标距离
const badgePostion = document.getElementById('badge').getBoundingClientRect();

var xDist = badgePostion.left - ballPosition.left; 
var yDist = badgePostion.top - ballPosition.top;
left表示dom对象左边到屏幕左边的距离
right表示dom对象右边到屏幕左边的距离
botton表示dom对象底部到屏幕上边的距离
top表示dom对象顶部到屏幕上边的距离

.执行上下文 闭包 执行栈 作用域链

什么是执行上下文: 执行上下文是JS代码运行的环境,代码运行前,必然先创建执行上下文
执行上下文的分类: 全局 函数 Eval
什么是执行栈: 执行栈是执行上下文的存储区域,用以管理执行上下文,遵循先进后出

执行上下文的创建:
执行上下文创建时有三大工作: this绑定,创建词法作用域,创建变量作用域

词法作用域包括环境记录器和外部环境引用
环境记录器其实就是变量对象,执行时叫做活动对象,存储变量和函数声明
外部环境引用其实就是作用域链,他是一个数组,数组的元素是变量对象,在函数定义的时候,这里存储了定义时所处上下文(父级上下文)的变量对象以及爷爷级上下文的变量对象,一直往上推,直到全局上下文的变量对象,而当前上下文的变量对象在函数执行前存储,这也是闭包的原理,销毁的上下文的变量早在定义阶段已经存储在函数的作用域链中了
什么是闭包: 可以访问自由独立变量(创建变量的上下文已经销毁)的函数

变量作用域其实也是词法作用域,只不过存储的是let const定义的变量,而词法作用域存储的是var定义的变量

this的指向问题

  • 对于普通函数,谁调用函数,this就指向谁,执行时确认(调用时,如果函数名前面没有任何对象,那就是全局对象调用的,浏览器中为window)
  • 对于箭头函数,this等于父级上下文(声明时所处上下文的this 父级上下文的this还要等运行时确定)中的this
  • 在定时器中,如果回调函数是普通函数,this永远指向window,如果是箭头函数,this等于定时器父级上下文(定时器声明时所处上下文)中的this(这个this要等运行时确定)

10.原型

每个函数在创建的时候,都会默认添加prototype属性,他是一个对象,这个对象的作用是为该函数创建的实例提供共享的属性和方法,它叫做原型对象
原型对象中的constructor属性指向它所属的函数,函数创建的实例对象中的__proto__属性指向原型对象
原型对象的__proto__属性也指向一个原型对象,对象和 对象的原型对象 以及 原型对象的原型对象构成了原型链
当我们访问对象的属性和方法时,如果在对象中没有找到,会去原型链中查找

  • 方法都由new Function() 得来 包括Function Object Array 所以方法的__proto__都等于Function.prototype
  • Function也是由Function构造的,所以Function的构造函数是Function,Function函数对象的__proto__上属性指向构造函数的原型对象,即Function的prototype,所以Function.proto === Function.prototype
  • Function.prototype的构造函数是Object()
  • 函数对象和普通对象 Function实例就是函数对象,其他都是普通对象
  • prototype是函数对象独有的,给其他对象提供共享属性
  • 每个对象都有__proto__属性,指向原型对象

继承的方式

1.原型链继承

// 原型链继承
/*  缺点: 1.构造函数不能传参 2.引用类型的属性被所有实例共享,如arr属性*/
function Person(name,age) {
	this.name = 'zs'
	this.age = 2
	this.arr = [1,2,3]
}

function Student(){}; 

Student.prototype = new Person();
Student.prototype.constructor = Student

var s1 = new Student();
var s2 = new Student();

s1.arr.push(4);
console.log(s1.arr)
console.log(s2.arr)

2.借用构造函数继承

// 借用构造函数继承
/*  缺点: 1.原型中的方法无法访问 必须写在构造函数内部 */
var array = [1,2,3]
var array1 = [1,2,3]

function Person(name,age,array) {
	this.name = 'zs'
	this.age = 2
	this.arr = array
}
Person.prototype.sayHello = function() {
	console.log('hello: ' + this.name);	
}

function Student(name,age,array){

	Person.call(this,name,age,array);

}; 



var s1 = new Student('zs',20,array);
var s2 = new Student('ww',21,array1);

s1.arr.push(4);
console.log(s1.arr)
console.log(s2.arr)

// s1.sayHello(); 原型中的方法无法访问

3.组合继承

// 缺点:  Student.prototype = new Person();  这步过程中使Student.prototype中也有了name ,age,等属性,是多余的
var array = [1,2,3]
var array1 = [1,2,3]

function Person(name,age,array) {
	this.name = 'zs'
	this.age = 2
	this.arr = array
}
Person.prototype.sayHello = function() {
	console.log('hello: ' + this.name);	
}

function Student(name,age,array){

	Person.call(this,name,age,array);

}; 


Student.prototype = new Person();
Student.prototype.constructor = Student;

var s1 = new Student('zs',20,array);
var s2 = new Student('ww',21,array1);

s1.arr.push(4);
console.log(s1.arr)
console.log(s2.arr)

 s1.sayHello();

4.寄生组合继承 解决上面组合继承的问题,其实就是封装一个空方法,让它的原型对象等于父级原型对象 ,然后new一个对象赋值给 子对象的原型对象 ,这样子对象的原型对象中就没有多余的属性了

var array = [1,2,3]
var array1 = [1,2,3]

function Person(name,age,array) {
	this.name = 'zs'
	this.age = 2
	this.arr = array
}
Person.prototype.sayHello = function() {
	console.log('hello: ' + this.name);	
}

function Student(name,age,array){

	Person.call(this,name,age,array);

}; 

// 解决组合继承的问题  使Student.prototype没有多余属性
// 为什么不直接让Student.prototype = Person.prototype呢? 因为这样会使Student原型中新增的属性和方法也存在于Person中,这显然是不合理的
// 关键部分
var F = function() {};
F.prototype = Person.prototype;
Student.prototype = new F();


Student.prototype.constructor = Student;

var s1 = new Student('zs',20,array);
var s2 = new Student('ww',21,array1);

s1.arr.push(4);
console.log(s1.arr)
console.log(s2.arr)

s1.sayHello(); 

new的工作与模拟

新建对象
对象原型属性指向构造函数的原型对象
构造函数的 this指向新建对象
返回对象(这里要注意 如果构造函数返回了对象 这个对象会替代新建的对象)

		function factory() {
			// 新建对象
			var obj = {};
			// 方法传入的第一个参数是构造函数,获取这个构造函数
			var contructor = [].shift.call(arguments);
			// 设置obj的原型属性
			obj.__proto__ = contructor.prototype;
			// 改变obj的this指向
			var ret = contructor.apply(obj,arguments);

			// 如果构造函数返回对象 作为最终的返回替代obj  如果构造函数返回不是对象  最终返回obj
			return (typeof ret) === 'object' ? ret : obj;
		}

		function Person(name,age) {
			this.name = name
			this.age = age

/*
			return {
				name1: name,
				age1: age
			}*/
		}
		Person.prototype.sex = 'male';


		var result = factory(Person,'zs',20);

		console.log(result.name)
		console.log(result.age)
		console.log(result.sex)
		console.log(result.name1)
		console.log(result.age1)

13.call,apply,bind以及模拟实现

都是改变this指向,都可以传参数
call直接调用,参数为列表
apply直接调用,参数为数组
bind返回方法,参数为列表
thisArg值被指定为 null 或 undefined 时this值会自动替换为指向全局对象,原始值则会被自动包装,也就是new Object()

Function.prototype.call2 = function(thisArg) {
	// 第一个参数为执行时的this,没传或为null时,自动替换为全局对象,原始值会被包装
	if (typeof thisArg === 'undefined' || thisArg === null) {
		thisArg = window;
	}
	// 获取参数列表
	var arg = [].slice.call(arguments,1);
	
	var arr = [];
	for (var i = 0;i < arg.length;i++) {
		arr.push('arg[' + i + ']');
	}
	thisArg = new Object(thisArg);
	var __fn = '__fn';
	thisArg[__fn] = this;
	var ret = eval('thisArg[__fn](' + arr + ')');
	delete thisArg[__fn];
	return ret;
}
Function.prototype.apply2 = function(thisArg,arrList)
   // 判断传入的数组 默认为空数组 只能是数组
 
   if (typeof arrList === 'undefined' || arrList === null) {
     arrList = [];
  
   if (typeof arrList !== 'object') {
     throw new TypeError('need a array')
  
   // 判断thisArg 如果未传值 或者传null 默认为去全局对象
   if(typeof thisArg === 'undefined' || thisArg === null) {
     thisArg = window;
  
   // 其他值会作为对应包装对象
   thisArg = new Object(thisArg
   // 加入方法
   var __fn = Date.now();
   thisArg[__fn] = thi
   var arr = [];
   for (var i = 0;i < arrList.length; i++) {
     arr.push('arrList[' + i + ']');
   }
  
   var ret = eval('thisArg[__fn](' + arr + ')');
   //var ret = thisArg[__fn](...arrList);
   delete thisArg[__fn];
   return ret;
}
   Function.prototype.bind2 = function(thisArg) {

      var self = this;  // this代表使用bind 的函数
      var args = [].slice.call(arguments,1)  // 获取bind时的参数列表

    var fbound = function() {
      // 获取执行时的参数列表
      var arr = [].slice.call(arguments)
      // 改变this指向 并传入参数
      // 改变this指向的时候,需要注意两种情况
     // 当作为构造函数时,this 指向实例,self 指向绑定函数,因为下面一句 fbound.prototype = this.prototype;,已经修改了 fbound.prototype 为 绑定函数的 prototype,此时结果为 true,当结果为 true 的时候,this 指向实例。
 //  fbound.prototype = 绑定函数的 prototype 意味着fbound实例的__proto__指向绑定函数的 prototype ,提供绑定函数的 prototype的constructor属性找到构造函数为绑定函数,即self 所以能确定this instanceof self为true或false
        // 当作为普通函数时,this 指向 window,self 指向绑定函数,此时结果为 false,当结果为 false 的时候,this 指向绑定的 thisArg。
        self.apply(this instanceof self ? this : thisArg, args.concat(arr));
    }

// 在fbound继承了self ,所以当作为构造函数的时候,new fbound 是self的一个实例
    var F = function() {};
    F.prototype = self.prototype;
    fbound.prototype = new F();

    return fbound;
}

同步异步 promise

  • 什么是同步: 同步是堵塞操作。上一步没执行完,不能执行下一步
  • 什么是异步: 异步不堵塞,上一步没执行完,也继续执行下面的代码
  • 什么是回调函数: 回调是一个函数,它作为参数传递给另一个函数,并在其父函数完成后执行。(有同步回调,也有异步回调)


JS中的异步操作有定时器,事件 ,Ajax,js是单线程的,遇到异步操作,将异步函数添加到事件队列中,具体的异步操作(比如定时器记时,http访问)不是JS引擎执行的,而是浏览器的其他线程执行的(定时器线程、事件触发线程、异步http请求线程),当异步操作完成后,如果JS引擎主线程空闲,就执行事件队列中的异步函数(先进先出),但是还要注意宏任务与微任务的区别,promise是微任务,定时器是宏任务,先执行所以的微任务,然后执行一个宏任务,然后再扫描,执行所以微任务,再执行宏任务。。。

  • 什么是promise: 从语法上说,promise是一种异步编程的解决方案,用以解决异步操作的地狱回调,语法上是一个对象,创建就执行
  • then方法是异步的,返回一个新的Promise对象,下面是具体的返回情况
返回了一个值,那么 then 返回的 Promise 将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。
没有返回任何值,那么 then 返回的 Promise 将会成为接受状态,并且该接受状态的回调函数的参数值为 undefined。
抛出一个错误,那么 then 返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。
返回一个已经是接受状态的 Promise,那么 then 返回的 Promise 也会成为接受状态,并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。
返回一个已经是拒绝状态的 Promise,那么 then 返回的 Promise 也会成为拒绝状态,并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。
返回一个未定状态(pending)的 Promise,那么 then 返回 Promise 的状态也是未定的,并且它的终态与那个 Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。
  • promise 的 .then 或者 .catch 可以被调用多次,但这里 Promise 构造函数只执行一次
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('once')
    resolve('success')
  }, 1000)
})

const start = Date.now()
promise.then((res) => {
  console.log(res, Date.now() - start)
})
promise.then((res) => {
  console.log(res, Date.now() - start)
})   // 这里执行两次then方法

const promise = new Promise((resolve, reject) => {
  resolve('success1')
  reject('error')
  resolve('success2')   // 这里第二次的resolve方法不起作用
})
  • return error对象并不会被catch捕获,throw才会
Promise.resolve()
  .then(() => {
    return new Error('error!!!')
  })
  .then((res) => {
    console.log('then: ', res)
  })
  .catch((err) => {
    console.log('catch: ', err)
  })
  • catch方法等同于then(null,(rejection)=>{})
  • promise的使用:
// then的用法  绑定resolve方法和reject方法  第一个方法resolve 第二个reject 通过在then中返回promise对象(就是then的链式操作),解决地狱回调问题
// catch方法  和reject回调的作用一样,但是多了个功能,在resolve报错的时候,也不会直接卡死,也会进入到catch

Promise.resolve(1)
  .then((res) => {
    console.log(res)
    return 2   
  })      // 这里的操作结果等于Promise.resolve(2)
  .catch((err) => {   // 等于.then(null,(reject)=>{})
    return 3
  })                  // 因为没有resolve的函数,所以等于没有执行,结果还是Promise.resolve(2)
  .then((res) => {
    console.log(res)
  })

深拷贝与浅拷贝

浅拷贝就只是复制对象的引用,如果拷贝后的对象发生变化,原对象也会发生变化。只有深拷贝才是真正地对对象的拷贝。
深拷贝创建一个全新的对象,然后为对象的元素赋值,通常使用递归方法进行深拷贝,以保证对象中的对象也是深拷贝

function deepClone(source){
	//判断复制的目标是数组还是对象
 	const targetObj = source.constructor === Array ? [] : {}; 
  	for(let keys in source){ // 遍历目标
  		if(source.hasOwnProperty(keys)){
  			// 如果值是对象,就递归一下
    	 		if(source[keys] && typeof source[keys] === 'object'){ 
    	 			targetObj[keys] = source[keys].constructor === Array ? [] : {};
    	 			targetObj[keys] = deepClone(source[keys]);}
       		else{ // 如果不是,就直接赋值
       			targetObj[keys] = source[keys];
       		}
       	} 
  		}
   	return targetObj;
}

JSON.stringify/parse的方法也可以进行深拷贝,但是undefined、function、symbol 会在转换过程中被忽略。。。

隐式转换

对于对象,先使用valueof方法转化位原始值,如果没能转化,再调用toString方法转化成string,Date对象除外,先使用toString方法转化
Number String Boolean Date 对象使用valueof方法会转化成原始值,其他是对象本身,所以需要调用string转化成字符串
原始值向数字转换:
undefined -> NaN
null -> +0
bool -> true 1 false 0
string -> 有字符串解析为数字,例如:‘324’转换为324,‘qwer’转换为NaN

原始值向字符串转化:
undefined -> 'undefined'
null -> 'null'
bool -> 'true' 或'false'
数字: -> 数字转换字符串,比如:1.765转为’1.765’

五种值转化为false:
0 undefined null NaN ''
null == undefined 为true
typeof取值: number string boolean undefined symbol object function

location对象

location对象是window对象下的一个属性,使用的时候可以省略window对象
location对象表示地址栏,用来设置URL

var btn = document.getElementById('btn');
		btn.onclick = function () {
			//location.href = 'http://www.baidu.com';
			//location.assign('http://www.baidu.com');
			location.replace('http://www.baidu.com'); //与assign的区别是不记录历史
			//location.reload(true); //true表示强制从服务器端获取 (ctrl+f5)false有可能从缓存获取页面(f5)
		}

返回浏览器的信息

history对象

历史信息

	<script>
		var btn = document.getElementById('btn');
		btn.onclick = function () {
			//history.go(-1);
			//history.go(1);
			history.forward();
                        history.back();     
		}
	</script>
posted @ 2020-04-17 17:03  zero博士  阅读(190)  评论(0)    收藏  举报