JS知识库


# 基础知识点

# 正则表达式

需求一:删除开头和结尾的空白

思路,找到空白,然后干掉它

let hello = "   Hello, World!  ";
// 注意要全局匹配,不然会寄
let wsRegex = /^\s+|\s+$/g;
let result = hello.replace(wsRegex,"");

需求二:限制可能的用户名

规则:
1.用户名只能是数字字母字符。
2.用户名中的数字必须在最后。 数字可以有零个或多个。 用户名不能以数字开头。
3.用户名字母可以是小写字母和大写字母。
4.用户名长度必须至少为两个字符。 两位用户名只能使用字母。

let username = "JackOfAllTrades";
let userCheck = /^[a-z][a-z]+\d*$|^[a-z]\d\d+$/i;
let result = userCheck.test(username);

# ES6语法

使用箭头函数编写简洁的匿名函数

有很多情况我们只会使用一次这个函数,那么我们就可以编写一个匿名函数

const myFunc = function(){
	//myFunc是常量名,不是函数名
	const myVar = "value";
	return myVar;
}

ES6 提供了其他写匿名函数的方式的语法糖。 你可以使用箭头函数

const myFunc = () => {
	const myVar = "value";
	return myVar;
}

如果只返回一个值的话还有简化的写法

const myFunc = () => "value";
  • 箭头函数可以传参用参
  • 箭头函数可以给参数设置默认值

rest操作符

...args 展开操作符可以用于创建有一个变量来接受多个参数的函数。 这些参数被储存在一个可以在函数内部读取的数组中。

计算数组的最大值
way1

var arr = [6,89,3,45];
var maximus = Math.max.apply(null,arr);
console.log(maximus);
// 89

way2

var arr = [6,89,3,45];
var maximus = Math.max(...arr);
console.log(maximus);
// 89

使用解构赋值来获取对象的值

// ES5语法
const user = { name: 'John Doe', age: 34 };
const name = user.name;
const age = user.age;
// ES6语法
const {name,age} = user;

还可以解构嵌套对象中的值。

// ES5
const user = {
  johnDoe: { 
    age: 34,
    email: 'johnDoe@freeCodeCamp.com'
  }
};

这是解构对象的属性值赋值给具有相同名字的变量:

// ES6
const {johnDoe:{age,email}} = user;

这是将对象的属性值赋值给具有不同名字的变量:

const {johnDoe:{age:userAge,email:userEmail}} = user;

使用解构赋值从数组中分配变量

选择元素来给变量赋值

const [a,b] = [1,2,3,4,5,6];
console.log(a,b);
// 1 2

通过逗号分隔符获得自己想要的值

const [a,b,,,c] = [1,2,3,4,5,6];
console.log(a,b,c);
// 1 2    5

使用解构赋值配合 rest 操作符来重新分配数组元素

const [a,b,...arr] = [1,2,3,4,5,7];
console.log(a,b);
console.log(arr);
// 1,2
// [3,4,5,7]

使用简单字段编写简洁的对象字面量声明

// ES5
const getMousePosition = (x,y) => ({
	x:x,
	y:y
});
// ES6
const getMousePosition = (x,y) => ({x,y});

导出与导入

  • 直接用export在文件里导出
  • 导入所有文件import * as balabala from '路径',把balabala作为一个总函数就好了
  • 用 export default 创建一个默认导出

Promise

Promise 是异步编程的一种解决方案 - 它在未来的某时会生成一个值。 任务完成,分执行成功和执行失败两种情况。

const myPromise = new Promise((resolve, reject) => {

});

Promise 有三个状态:pending、fulfilled 和 rejected。 上一个挑战里创建的 promise 一直阻塞在 pending 状态里,因为没有调用 promise 的完成方法。 Promise 提供的 resolve 和 reject 参数就是用来结束 promise 的。 Promise 成功时调用 resolve,promise 执行失败时调用 reject.

Promise 的then方法

// result即为传入resolve的参数
myPromise.then(result => {

});

使用 catch 处理 Promise 失败的情况

当 promise 失败时会调用 catch 方法。 当 promise 的 reject 方法执行时会直接调用。

myPromise.catch(error => {

});

# this相关问题

运行时this永远指向最后调用它的那个对象。

谁new了那个对象,this就指向谁

在其他函数内部定义立即执行函数,this默认window

this的基本绑定

function foo() {
  console.log(this.a)
}
var a = 1
foo()

const obj = {
  a: 2,
  foo: foo
}
obj.foo()
const c = new foo()

1.对于直接调用 foo 来说,不管 foo 函数被放在了什么地方,this 一定是 window
2.对于 obj.foo() 来说,我们只需要记住,谁调用了函数,谁就是 this,所以在这个场景下 foo 函数中的 this 就是 obj 对象
3.对于 new 的方式来说,this 被永远绑定在了 c 上面,不会被任何方式改变 this
4.不管我们给函数 bind 几次,fn 中的 this 永远由第一次 bind 决定


image

this的顺序规则:

1.函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象。

var bar = new foo();

2.函数是否通过call、apply(显式绑定)或者硬绑定调用?

var bar = foo.call(obj2);

3.函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。

var bar = obj1.foo();

4.如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。

 var bar = foo();

bind(),apply(),call()

call()方法与apply()方法返回的结果是完全相同的,至于是使用 apply()还是 call(),完全取决于你采取哪种给函数传递参数的方式最方便。

  • 参数数量/顺序确定就用call,参数数量/顺序不确定的话就用apply。
  • 考虑可读性:参数数量不多就用call,参数数量比较多的话,把参数整合成数组,使用apply。

bind()方法会创建一个函数的实例,其 this 值会被绑定到传给 bind()函数的值。意思就是 bind() 会返回一个新函数。

image

# DOM核心

# 关于DOM操作,主要有创建,增,删,改,查,属性操作,事件操作。

# (1)创建

1.document.write
2.innerHTML
3.createElement

State:
1.document.write 是直接将内容写入页面的内容流,但是文档流执行完毕会导致页面全部重绘,只保留html标签和写入内容。
2.innerHTML 是将内容写入某个DOM节点,不会导致页面全部重绘。
3.innerHTML 创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂。

innerHTML在不使用拼接字符串的情况下,比创造元素拼接元素要快一些。 4.虽然createElement()创建多个元素慢一些,但是结构更清晰。

# (2)增

1.appendChild

// 此方法在node节点「父亲节点」里,顺序增加一个节点
node.appendChild(child)

2.insertBefore

// 此方法在node节点「父亲节点」里,在开头增加一个节点
node.insertBefore()

# (3)删

1.removeChild

//此方法从DOM中删除一个子节点,返回删除的节点。
node.removeChild()

# (4)改

主要修改dom的元素属性,dom的内容,属性,表单的值等

修改内容 index
元素属性 src,href,title
普通元素内容 innerHTML,innerText
元素样式 style,className
表单内容 value,type,disabled

# (5)查

1.DOM提供的API方法:getElementById,getElementByTagName(古老用法)
2.H5提供的新方法:querySelector,querySelectorAll
3.利用节点操作获取元素:父(parentNode),子(children),兄(previousElementSibling,nextElementSibling)

# (6)属性操作

1.setAttribute:设置dom的属性值 2.getAttribute:得到dom的属性值 3.removeAttribute移除属性

# (7)事件操作

鼠标事件 触发条件
onclick 鼠标点击触发
onmouseover 鼠标经过触发
onmouseout 鼠标离开触发
onfocus 获得鼠标焦点触发
onblur 失去鼠标焦点触发
onmousemove 鼠标移动触发
onmouseup 鼠标谈起触发
onmousedown 鼠标按下触发

# BOM核心

元素偏移量 offset 系列

概述:offset 翻译过来就是偏移量, 我们使用 offset 系列相关属性可以动态的得到该元素的位置(偏移)、大小等。
(1)获得元素距离带有定位父元素的位置
(2)获得元素自身的大小(宽度高度)
(3)注意:返回的数值都不带单位

offset系列属性 作用
element.offsetParent 返回作为该元素带有定位的父级元素 如果父级没有定位则返回body
element.offsetTop 返回元素相对带有定位父元素上方的偏移
element.offsetLeft 返回元素相对带有定位父元素左方的偏移
element.offsetWidth 返回自身包括padding,边框,内容区的宽度,返回数值不带单位
element.offsetHeight 返回自身包括padding,边框,内容区的宽度,返回数值不带单位

offset与style区别

offset style
offset 可以得到任意样式表中的样式值 style 只能得到行内样式表中的样式值
offset 系列获得的数值是没有单位的 style.width 获得的是带有单位的字符串
offsetWidth 包含padding+border+width style.width 获得不包含padding和border 的值
offsetWidth 等属性是只读属性,只能获取不能赋值 style.width 是可读写属性,可以获取也可以赋值
所以,我们想要获取元素大小位置,用offset更合适 所以,我们想要给元素更改值,则需要用style改变

image

元素可视区 client 系列

概念:client 翻译过来就是客户端,我们使用 client 系列的相关属性来获取元素可视区的相关信息。通过 client 系列的相关属性可以动态的得到该元素的边框大小、元素大小等。

client系列属性 作用
element.clientTop 返回元素上边框的大小
element.clientLeft 返回元素左边框的大小
element.clientWidth 返回自身包括padding,内容区,不包括边框的宽度,返回数值不带单位
element.clientHeight 返回自身包括padding,内容区,不包括边框的宽度,返回数值不带单位

image

元素可视区 scroll 系列

scroll系列属性 作用
element.scrollTop 返回被卷去的上侧距离,返回数值不带单位
element.scrollLeft 返回被卷去的左侧距离,返回数值不带单位
element.scrollWidth 返回自身实际的宽度,不含边框,返回数值不带单位
element.scrollHeight 返回自身实际的宽度,不含边框,返回数值不带单位

image


image

offset/client/scroll的主要用法
1.offset系列 经常用于获得元素位置 offsetLeft offsetTop
2.client 经常用于获取元素大小 clientWidth clientHeight
3.scroll 经常用于获取滚动距离 scrollTop scrollLeft
4.注意页面滚动的距离通过 window.pageXOffset 获得


触屏事件概述

image


触摸事件对象(TouchEvent)
TouchEvent 是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。这类事件用于描述一个或多个触点,使开发者可以检测触点的移动,触点的增加和减少,等等
touchstart、touchmove、touchend 三个事件都会各自有事件对象。
触摸事件对象重点我们看三个常见对象列表:

image

因为平时我们都是给元素注册触摸事件,所以重点记住 targetTocuhes

移动端拖动元素

[1]touchstart、touchmove、touchend 可以实现拖动元素
[2]但是拖动元素需要当前手指的坐标值 我们可以使用 targetTouches[0] 里面的pageX 和 pageY
[3]移动端拖动的原理:手指移动中,计算出手指移动的距离。然后用盒子原来的位置 + 手指移动的距离
[4]手指移动的距离:手指滑动中的位置 减去 手指刚开始触摸的位置
拖动元素三步曲:
(1)触摸元素 touchstart: 获取手指初始坐标,同时获得盒子原来的位置
(2)移动手指 touchmove: 计算手指的滑动距离,并且移动盒子
(3)离开手指 touchend:
注意: 手指移动也会触发滚动屏幕所以这里要阻止默认的屏幕滚动 e.preventDefault();

# 奇奇怪怪的bug

  1. 使用if else if else这类条件语句的时候,如果你用了=而不是==,恭喜你,你寄了。

一般来说=会给你判断正确,不会走到后面的else里面。
JavaScript会把大部分的值都视为true,除了所谓的“falsy值”,即false,0,""(空字符串),NaN,null

  1. typeof返回的是字符串!!!

  2. st[i][0].toUpperCase()会全部大写,需要把后面的一部分操作一下

# 《你不知道的JavaScript》读书笔记

let,var and const

1.let
(1)let 如果是在作用块中定义的变量,不会提升「如果先用后定义会报错,undefined」,并且作用域也不变。
(2)let不可以重复声明,否则会报错;var则可以。

建议:写代码的时候加一对花括号来“显式”标明这个作用块

2.var 如果是在作用块中定义的变量,会提升到该作用块的开头部分「但只会提前声明而不会提前赋值」,同时作用域上升一级。

3.const 定义一个变量,声明变量时必须初始化变量,同时const也不能被重复声明
如果这个量被改变会报错
不过如果const声明的是一个对象,那改变对象里面的值并不算修改了这个对象,所以可以,也并不会报错。

IFFE:立即执行函数

IFFE(Immediately Invoked Function Expression):立即执行函数

在书上看到的内容巧合的出现在了JavaScript的专业课程上,有种不期而遇的幸福感
立即执行函数有两种执行方式:
1.(fun{….}());
2.(fun{….})();
好处:不会污染全局作用域

LHS and RHS

1.LHS:赋值操作的目标是谁「左侧」
2.RHS(retrieve his source value):谁是赋值操作的源头「非左侧」

提升

var a = 2;

这段代码可以拆分成

var a

a = 2

两个语句。
第一个语句自动提升到该作用域的最顶部,
而第二个语句保留在原位置:“使用var时,没有一个一个变量是未声明的”

1.函数提升优先于变量提升,函数提升会把整个函数挪到作用域顶部,变量提升只会把声明挪到作用域顶部
2.var 存在提升,我们能在声明之前使用。let、const 因为暂时性死区的原因,不能在声明前使用
3.var 在全局作用域下声明变量会导致变量挂载在 window 上,其他两者不会 let 和 const 作用基本一致,但是后者声明的变量不能再次赋值

闭包

理解:

闭包使得函数可以从外部访问定义时的词法作用域。

识别闭包:

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

典型例子:

function foo() {
	var a = 2;

	function bar() {
		console.log( a );
	}
	return bar;
}
var baz = foo();
baz();//console.log(a) ==> 2

块作用域与闭包结合
需求:从1打印到5

代码1:

for(var i = 1 ; i <= 5; i++){
     (function(){
       	 setTimeout(function timer(){
                console.log( i );
            },i*1000);
            })();
    	}
		// 6 6 6 6 6

原因:
一直输出6是因为i是共用的,执行完了五次循环之后,创建了五个事件之后,i=6

代码2:

for(var i = 1 ; i <= 5; i++){
        (function(j){
           // var j = i;
            setTimeout(function timer(){
				console.log( j );
            },j*1000);
        })( i );
    }
	//1 2 3 4 5

原因:
每次创建这个函数的时候都有一个传入值,不会复用i

代码3:

  for(let i = 1 ; i <= 5; i++){
        (function(){
            setTimeout(function timer(){
               console.log( i );
            },i*1000);
        })();
    }
// 1 2 3 4 5 

原因:
每次创建这个函数的时候let都创建了一个块作用域,i是小的作用域里的,已经覆盖掉了原本的i,所以没问题

# JS知识点每日一题

# JS原始数据类型有哪些?引用数据类型有哪些?

# 七大原始值

1.boolean
2.symbol
3.null
4.undefined
5.string
6.number
7.bigint

# 引用数据类型

对象:Object(包含普通对象-Object,数组对象-Array,正则对象-RegExp,日期对象-Date,数学函数-Math,函数对象-Function)

# this的指向问题

# this隐式绑定的情况

1.全局上下文
2.直接调用函数
3.对象.方法的形式调用
4.DOM事件绑定(特殊)
5.new构造函数绑定
6.箭头函数

# 全局上下文

全局上下文默认this指向window, 严格模式下指向undefined。

# 直接调用函数

直接赋值然后调用

See More
let obj = {
  zzk: function() {
    console.log(this);
  }
}
let func = obj.zzk;
func();

直接调用this相当于全局上下文

# 对象.方法的形式调用

obj.zzk();

这就是对象.方法的的情况,this指向对象

# DOM事件绑定

onclick和addEventerListener中 this 默认指向绑定事件的元素。

IE比较奇异,使用attachEvent,里面的this默认指向window。

# new+构造函数

此时构造函数中的this指向实例对象。

# 箭头函数?

箭头函数没有this, 因此也不能绑定。里面的this会指向当前最近的非箭头函数的this,找不到就是window(严格模式是undefined)。比如:

See More
let obj = {
  a: function() {
    let do = () => {
      console.log(this);
    }
    do();
  }
}
obj.a(); 
// 找到最近的非箭头函数a,a现在绑定着obj, 因此箭头函数中的this是obj

优先级: new > call、apply、bind > 对象.方法 > 直接调用。

# 分析实例

eg1:

See More
function a(){
	function b(){
		console.log(this);
		function c() {
			"use strict";
			console.log(this);
		}
		c();
	}
	b();
}
a();
//输出为 Winodw和undefined;
//执行顺序是首先调用a()方法,然后嵌套着调用b()方法,此时console.log(this)为全局--> Window
//调用b()方法的时候又会调用c()方法,在function c()中使用了严格模式,在严格模式中默认this为undefined
//其实就是第一个是全局,第二个是严格模式的默认值

eg2:

See More
var name = '小白';

function special() {
	console.log('姓名:'+this.name);
}

var girl = {
	name:'zzk',
	detail: function () {
		console.log('姓名:'+this.name);
	},
	woman: {
		name:'cxz',
		detail: function(){
			console.log('姓名:'+this.name);
		},
	},
	special:special,
}

girl.detail();
//ans:zzk
//这是对象.方法的情况
//this指向这个对象
girl.woman.detail();
//ans:cxz
//看后面两个,还是对象.方法的情况,this指向这个对象
girl.special();
//ans:zzk
//这是对象.方法的情况
//this指向这个对象

# 原始类型有哪几种?null 是对象嘛?

答:
原始数据类型有6种,分别是:“Symbol,number,String,null,undefined,boolean.”
用typeof来查看null的话,显示的确实是Object,但其实这是历史遗留的Bug。
在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,
000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object

# 浅拷贝与深拷贝

(1).浅拷贝:浅拷贝是创建一个新对象,这个对象有原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,将会影响到另一个对象。
(2).深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。