前端基础

前端基础这篇文章主要是对 html+css+JavaScript 三件套的一个深度知识总结,篇幅会较长,力求做到知识点不遗漏,方便以后有遗忘的知识点的时候,可以通过快速的回顾笔记而熟悉起来。

前端关注点:美观、功能、无障碍、性能、兼容、安全、用户体验

我会不断更新这个笔记,让它质量越来越高

基础知识

浏览器历史

  1. 1990 蒂姆 伯纳斯 李 发明超文本

  2. 1993 伊利诺大学 MOSIAC 浏览器 可以显示图片

  3. 1994 网景公司 Netscape navigator

  4. 1996 微软收购 spy glass -> IE1.0; 网景公司开发 livescript; Java火起来,livescript不温不火,livescript改名为JavaScript

  5. 2001 IE6 XP诞生,出现 js 引擎

  6. 2003 mozilla公司 firefox -> netscape

  7. 2008 google基于webkit blink gears 开发Chrome,

    Chrome —> V8引擎,速度非常快,一是可以直接翻译机器码,二是独立于浏览器运行;v8的诞生,直接催化了 nodejs的诞生

  8. 2009 甲骨文Oracle收购SUN,JS所有权给甲骨文

主流浏览器

5大主流浏览器 内核
IE trident
chrome webkit -> blink
safari webkit
firefox gocko
opera presto -> blink

浏览器由渲染引擎和 JS引擎组成。

渲染引擎

浏览器所采用的「渲染引擎」也称之为「浏览器内核」,用来解析 HTML与CSS,决定了浏览器如何显示网页的内容以及页面的格式信息。

渲染引擎是浏览器兼容性问题出现的根本原因。

JS引擎

浏览器本身并不会执行JS代码,而是通过内置 JavaScript 引擎(解释器) 来执行 JS 代码 。

JS 引擎执行代码时会逐行解释每一句源码(转换为机器语言),然后由计算机去执行。所以 JavaScript 语言归为脚本语言,会逐行解释执行。

html

html,hypertext markup language,超文本标记语言, 不是一种编程语言,是一种描述性的标记语言

编程语言是有编译过程的,而标记语言没有编译过程,HTML标签是直接由浏览器解析执行

要理解html标签的语义化特性。比如,面试的时候问你,<h1> 标签有什么作用?

  • 正确答案:给文本增加主标题的语义。
  • 错误答案:给文字加粗、加黑、变大。

HTML标签是分等级的,HTML将所有的标签分为两种:

  • 文本级标签:p、span、a、b、i、u、em。文本级标签里只能放文字、图片、表单元素。(a标签里不能放a和input)
  • 容器级标签:div、h系列、li、dt、dd。容器级标签里可以放置任何东西。

div的语义是division“分割”; span的语义就是span“范围、跨度”。

  • div标签:可以把标签中的内容分割为独立的区块。必须单独占据一行。
  • span标签:和div的作用一致,但不换行。

pre标签:保证标签内空格不被合并,真正排网页过程中,<pre>标签几乎用不着

实体字符

  • &nbsp 空格 (non-breaking spacing,不断打空格)
  • &lt 小于号< ; &gt 大于号>

超链接

  • title:鼠标悬停时呈现出的文本。

  • name:主要用于设置一个锚点的名称。

  • target:告诉浏览器用什么方式来打开目标页面

    • _self:在同一个网页中显示(默认值)
    • _blank在新的窗口中打开

锚链接:

  1. 一个完整的URL由 schme+host+path+query+hash组成,hash就是在页面不变的情况下,对于浏览数据的位置变动
  2. 视音频标签:video标签是html自带的视频标签,有src、controls、autoplay、muted等属性,track标签是视频字幕标签;audio是自带的音频标签
  3. 页面标签:一个页面可以分header、main、aside和footer四个页面标签,article 和section也是两个对应的内容标签
  4. 列表标签:<ul> unorder-list ,<ol> order-list,<li> list-item
  5. 导航标签:<nav>
  6. 表格标签:<thead>表头,<tbody>表格内容,<tr> 表行,<th> 表头单元格,<td> 表格数据单元格;rowspan属性,跨越多行,colspan属性,跨越多列
  7. 表单标签:form;input的type有text,password,number,date,file等;textarea,多行文本框,rows cols属性指定文本框最大行列数
  8. 表单单选框,input的type=radio,name属性决定了哪些互斥;checkbox多选框,checked属性是否默认选中
  9. 下拉选择框,select,下来选择项,option
  10. link 加载预先资源
  11. microdata

根据内容选择合适的标签!

html中的换行是会带来文本分隔符的,这也是占据位置的

css简介

css,cascading style sheets,用来定义页面元素的样式,页面中使用css主要分为外联<link>、嵌入<style>和内联<p style=’’> </p>三种

css工作方式

选择器基础

:root 根选择器;*是通配选择器; #id,id选择器,一个id只能出现一次;.class,class类名选择器;属性选择器【】,常用于input;

1
2
3
4
5
[input='text']{
width=200px
}

<input type='text' />

选择器优先级

内联样式 > 内部样式 > 外部样式,在样式后加 !important 优先级最高

css优先级:!important > id | class > 标签 > *

CSS权重,极少用。内联样式 1000 ; id 100 ; class,属性,伪类 10 ;标签,伪元素 1 ; * 0

  1. 标签[属性],属性选择器,带有该属性的都被选择中,eg a[title] {}
    1. a [ href= ‘https://www.baidu.com'] {}
    2. ^= 搜寻以什么开头的值 a [ href ^= ‘https’] {}
    3. $= 搜寻以什么结尾的值 a [ href $= ‘.com’] {}
    4. *= 搜寻包含关键字的值 a [ href *= ‘baidu’] {}

选择器进阶

  1. 伪类选择器,对于普通选择器加以修饰

    a:visited{} 浏览过的;a:hover {} 悬浮在上面的;a:disabled,禁用的;a:focus,元素聚焦;

    a:first-child{} 选取第一个子元素;a:last-child{} 选取最后一个子元素;a:nth-child(4) {} 选取第四个子元素;a:nth-child(odd) {} 选取奇数项子元素;a:nth-child(even) {} 选取偶数项子元素;

  2. 选择器可彼此组合

    1. 后代组合的查找顺序是 从下至上,从右至左

    2. 直接组合 AB,满足A且满足B;

    3. 后代组合A B,满足A的所有B;

    4. 亲子组合,A>B,只选择A子元素的B

      • A B 和 A>B 的区别在于 子子元素 是否被选择
    5. .container + div 意思为 选取 同container父级 紧相邻 在其之后的 div元素

    6. .container ~ div 意思为 选取 同container父级 之后所有的div元素,即使中间有其他元素隔开

    7. var() 函数,用于插入自定义属性的值;自定义属性的名称(必须以两个破折号开头)

      定义一个名为 “–main-bg-color” 的自定义属性,然后使用 var() 函数在样式表中插入该自定义属性的值

样式基础

浏览器hack层叠原理

  1. 同一个属性,后书写的值会覆盖前面书写的值
  2. 对浏览器无效的属性会被忽略

css会自动继承父元素计算值,除非另外设置

min-width:用于浏览器宽度过小时,会将内容自动换行,设置min-width可以保证 浏览器宽度过窄时 不换行,有横向滚动条出现

overflow:常用于模型溢出,hidden、scroll、auto

字体相关:font-weight,normal、bold、lighter; fontsize:字体高度,宽度根据字体自动缩放;font-style 设置斜体;font-family设置字体类

边框:border: 1px solid #000 宽度、样式、颜色,transparent 透明,也是颜色的一种

文本水平居中:text-align:center;文本行高,line-height:默认22px;文本垂直居中,可以将文本行高设置为盒子的高度

输入距离,text-indent:2em,设置开始输入位置有两个空格;取消文本下划线,text-decoration: none

光标手势,cursor:pointer,not-allowed

display:inline(无边距无换行)、inline-block(有边距 无换行)、block(有边距,有换行)很重要

隐藏元素: visibility: hidden 保存位置;display:none不保留位置

行内块元素和行内元素文本对齐,vertical-align垂直方向对齐,top,middle,~px

background-colorbackground-imagebackground-repeat repeat默认,no-repeat;background-attachment 背景图片是否可滑动,默认scroll,fixed;background-positionbackground-size cover占满,多的裁掉,contain 自身宽高比不变,保证图片全显示,多的部分该留白留白;

background综合写法,color img repeat attachment position/ size

伪类和伪元素,一个冒号伪类,两个冒号即伪元素,没有太大的区别。::before 和 ::after必须加content属性,否则不生效;

z-index,数值越大,越靠近用户

如何画三角形

边框内部宽高设为0,边框三边取透明,只留一边即为三角形

image-20221123212615725

单行文本截断和显示省略号

white-space:nowrap 不换行;overflow:hidden;text-overflow:ellipsis 隐藏部分加省略号

兼容性写法

img 前缀-ms-的IE浏览器
img 前缀-moz-的火狐浏览器
img 前缀-webkit-的Google Chrome浏览器
img 前缀-webkit-的Safari浏览器
img 前缀-o-的Opera浏览器
img 前缀-xv-的Opera浏览器

总结就是 -ms 、-moz 、-webkit 、-o

css单位

image-20220922215343554
  1. px是绝对单位;em是相对单元,基于目前容器的大小进行设定(默认16px,文本字符默认21px),可以理解为倍数

  2. rem和em类似,但是它是基于root的html元素的大小设定去计算

  3. vw & vh,全程是 viewport width和viewport height,即用户看到的窗口,值在0 ~100之间

  4. vmin & vmax,vmin代表屏幕较短的一边,vmax代表屏幕较长的一边,概念与vw vh类似,常用于手机旋转屏设备

盒模型基础

盒子有块级盒子和行级盒子之分

display属性分为block、inline和inline-block,其中inline-block被放在行盒,但可以设置宽度,且不被拆分

  • 块级盒子block 不和其他盒子并列摆放
  • 行级盒子 inline 和其他行盒放在一行或拆开成多行,盒模型中的width、height不适用于行盒
  1. 块级元素生成块盒,body、article、div、main、section、h1-6…
  2. 行级元素生成行盒,span、em、strong、cite、code….

盒子阴影: box-shadow: 水平偏移,垂直偏移,模糊距离,阴影距离,inset向内加阴影。这里是先水平后垂直,和margin的先上下,后左右不一样

盒子圆角,border-radius 可以设置百分比和px;当盒子内部元素溢出,记得使用 overflow: hidden

overflow属性设置content溢出部分, visible可见、hidden不可见、scroll滑动可见

box-sizing设置元素的宽高度是否包含内外边距

  1. content-box 是默认值,不包含内外边距
  2. 为border-box时,长宽则会是包含margin和padding的大小,内容区实际宽度为 width 减去 (border + padding)

background-clip设置背景生效位置;border-box全盒生效;padding-box,内边距和内容生效;content-box,仅在内容生效

行内元素垂直对齐方式,利用表格元素table+vertical-align实现,父级给table,子级给table-cell,vertical-align: middle

margin塌陷

margin塌陷:margin 外边距在垂直方向上会采取margin collapse策略,即不合并,而取最大值;常见的如 子盒子想在父盒子垂直居中,设置margin,不会起作用,反而会让父级盒子跟着下移

解决办法:把父级盒子变成BFC

触发办法:

浮动元素, float 属性设置除 none 以外的值;
定位元素, position 属性设置绝对定位 absolute 或固定定位 fixed;
display 属性设置为其中之一的值 inline-block, table-cell, table-caption;
overflow 属性除 visible 以外的值, 即 hidden,auto,scroll;

如何设置内部盒子居中

外部盒子设置padding,内部盒子宽高都100%即可

css布局方式

  1. 常规流:行级、块级、flexbox、grid布局;2. 浮动流;3. 绝对定位

常规流 normal flow

除了根元素、浮动元素和绝对定位元素不在,其他都在常规流之内

行级排版上下文,Inline Formatting Context IFC,水平排版

块级排版上下文,Block Formatting Context BFC

flexible box

display: flex 生成块级flex容器;display: inline-flex 生成行级flex容器;

流向和换行

flex-diretion控制流向,默认是从左到右,flex-diretion的方向称为主轴,与其垂直的方向称为侧轴,参数row默认,column,row-reverse,column-reverse

flex-wrap 控制是否允许换行,参数 nowrap 默认不换行,wrap 弹性子项溢出会换行

flex-flow 是flex-direction和flex-wrap的缩写,flex-flow:row nowrap

元素布局

justify-content 控制元素主轴方向排版,参数flex-start、flex-end、center、space-between、space-around、space-evenly

align-items 控制弹性盒子元素在侧轴(纵轴)方向上的对齐方式,参数flex-start、flex-end、center、stretch(拉伸到父容器,默认值)、baseline

align-content 控制 **多行内容 **在侧轴方向排版,设置各个行的对齐,参数flex-start、flex-end、center、stretch(拉伸到父容器,默认值)、baseline(元素统一基准线)

弹性子元素属性

order 控制 DOM元素出现顺序,从小到大,默认都是0,可以为负值。

使用弹性盒子,居中变的很简单,只需要设置 margin: auto; 可以使得弹性子元素在两上轴方向上完全居中

align-self 控制覆写****元素自身 的侧轴排版,参数和align-items一致

flex 用于指定弹性子元素如何分配空间,flex = flex-grow + flex-shrink + flex-basis

  • flex-grow 控制有剩余空间时flex-item的放大比例;0表示默认大小
  • flex-shrink 控制空间不足时flex-item的缩小比例;
  • flex-basis 主轴方向的基础长度,auto刚好包裹元素

grid box

flex将元素按照单向进行摆放,grid则可以将元素按照两个方向进行摆放

描述 网格如何划分

display: grid或 inline-grid 生成网格容器;grid-template将容器划分为网格,再设置每一个子项占据哪些行、列

grid-template-columns 控制网格列划分,grid-template-rows 控制网格行划分

  • grid-template-columns: 100px 100px 100px,也可以写为repeat(3,100px)
  • grid-template-columns: 30% 30% auto
  • grid-template-columns: 100px 1fr 1fr 说明:fr,fraction 份数,即剩下可用空间的均分比例

网格间距:grid-gap = grid-column-gap + grid-row-gap

描述元素的位置

网格线都从1开始,并不是0。grid-column grid-column-startgrid-column-end 的简写属性;grid-row grid-row-startgrid-row-end 的简写属性

  • grid-column-start: 2;grid-column-end: 5
  • grid-column-start: 2;grid-column-end: span 3

grid-area 控制元素在网格中的位置

  • grid-area:1/1/3/3 左上角1,1和右下角3,3
  • grid-area: 2 / 1 / span 2 / span 3; 第二行跨越两格 第一列跨越三格
  • grid-areas 控制各个网格的名字,grid-areas: ‘header header header header’,repeat不适用于此

网格内元素布局

justify-items 控制元素在网格内水平方向上的布局;align-items 控制垂直方向上的布局

align-self,justify-self 控制单个元素的布局

justify-content 控制水平方向 网格总体布局,align-content 控制垂直方向 网格总体布局

非常推荐用下面这个小游戏来检验自己的学习成果!

Grid Garden - 一个用来学CSS grid的游戏 (runoob.com)

浮动流 float

浮动元素脱离常规流,漂浮在容器左边或右边,是inline-block;浮动元素后面的行盒会变短以避开浮动元素

通过float属性实现元素浮动,float属性定义元素在哪个方向浮动,它有两个值float:leftfloat:right,默认值为none

内联、内联块、浮动、溢出隐藏、纯文本都可以识别浮动元素的位置,但是块级元素无法识别

clear 属性控制元素 不受浮动元素影响

如何清除浮动

问题:父元素因为子级元素浮动引起的内部高度为0的问题

如何解决:after伪元素清除浮动。 在父元素上 加 clearfix 这个class

1
2
3
4
5
.clearfix::after{
content: "";
display: block;
clear: both;
}

绝对定位 position

position有四个取值:static、默认值,非定位元素;relative,相对自身原本位置偏移,原来的位置不腾出,不脱离文档流;

absolute,相对于最近的 relative或absolute进行绝对定位,腾出原来的位置,脱离文档流;

fixed,相对于浏览器窗口的绝对定位,会变成 内联块元素,需要额外赋宽度,不随页面滚动而变化,常用于导航栏

使用top、left、bottom、right设置偏移长度,流内其他元素当它没有偏移一样进行布局,即使会有遮挡

css transform 如何变形

对元素进行平移 translate、旋转 rotate、缩放 scale、倾斜 skew,transform不会对其他元素布局产生影响

  • transform :translate(100px, 100px)
  • transform :rotate(90deg) 顺时针旋转90度

css transition 过渡方式

transition-property 需要过渡效果的属性;transition-duration 过渡耗时;transition-timing-funtion 过渡效果;transition-delay 过渡延迟

  • transition:height 500ms linear 1s
  • transition-timing-funtion 有以下几种:linear、ease、ease-in、ease-out、ease-in-out、steps(4)

动画效果

css animation 动画

响应式设计,同一个页面适应不同屏幕大小设备的方案

  1. 设置viewport,<meta name=’viewport’ content=’width=device-width’, initial-scale=1.0’>
  2. contain 和 cover 的区别:contain,不造成裁剪,可能有留白;cover,可能会造成裁剪,不留白
  3. media query 针对不同屏幕,应用不同样式,@media screen and (min-width:480px){ } 在至少480px的屏幕上才使用该样式
    • 可以查询的media,width/
    • height,device-width/device-height,device-pixel-ratio、orientation

@keyframes

通过 @keyframes 规则创建动画。原理是将一套 CSS 样式逐渐变化为另一套样式。在动画过程中,可以多次改变这套 CSS 样式。

以百分比来规定改变发生的时间,或者通过关键词 “from” 和 “to”,等价于 0% 和 100%。

1
@keyframes animationname {keyframes-selector {css-styles;}}
描述
animationname 必需。定义动画的名称。
keyframes-selector 必需。动画时长的百分比。合法的值:0-100%from(与 0% 相同)to(与 100% 相同)
css-styles 必需。一个或多个合法的 CSS 样式属性。

JavaScript基础

查漏补缺

ECMA

ECMA, 欧洲计算机制造联合会,评估、开发计算机各项标准。其中ECMA -262是脚本语言的规范,ECMAScript。ES5,ES6都是从这里来的

字节码

ASCII码,1个字节, 表1 0-127,数字 小于 大写字母 小于 小写字母;表2 128-255。UNICODE码 涵盖ASCII码,2个字节

1
2
3
返回字符串第一个字符的 Unicode 编码(H 的 Unicode 值):
var str = "HELLO WORLD";
var n = str.charCodeAt(0);

可以通过charCodeAt() > 255,检查每个字符是否大于255,大于返回两个字节,小于等于返回一个字节

JS单线程引擎

JS引擎,是单线程,但是可以通过轮转时间片来模拟多线程

动态语言 -> 脚本语言 -> 解释型语言 -> 弱类型语言

静态语言 -> 编译型语言 -> 强类型语言

堆内存和栈内存

JS变量都存放在内存中,而内存给变量开辟了两块区域,分别为栈区域和堆区域

是一种 先进后出 的数据结构,像个容器,容量小速度快,但不灵活,变量使用完成后就可以将其释放,内存回收容易实现;

像个房间,容量较大,使用灵活,可以动态增加或删除空间,但是存取比较慢,存储变量时没有什么规律可言。

基本类型存放在栈内存,引用类型存放在堆内存中的,但是引用类型的引用还是存在栈内存

基本类型 声明一个变量,多次赋值就会取最后一个值,不能添加属性或者方法,但是Boolean、Number、String有自己的包装类
基本类型 可以直接复制,复制之后的内容和原内容没有什么联系,类似于开辟了一个新的空间

引用类型 直接赋值给另一个变量以后相互之间的修改会互相影响对方,进而引出浅拷贝与深拷贝的问题

基本语法

6种基本类型

Number、String、Boolean、undefined、null、Symbol (new in ES 6)

  • JavaScript的Number包含了浮点和整数型这些,根据具体的值来分配具体的类型,故JavaScript为弱类型语言
  • 原始值没有自己的方法和属性,但是Boolean、Number、String有自己的包装类,undefined和null没有
  • 原始值初始化赋值和内容都存放在栈内存中

引用类型

引用值统称为 Object 类型,又可以细分为:object 类型、array类型、date类型、RegExp 类型、function类型 等。

  • 引用值有自己的方法和属性,如 length、push等
  • 引用值初始化赋值和实际内容都存放在堆内存中,但指向堆内存中该对象的指针仍然存放在栈内存中
1
2
3
4
5
6
7
1. {} == {}  return false
// 对象是引用值,引用值对比的是地址,两个空对象存储在不同的地址,自然就不相等

2. 如何让两个对象相等
var obj = {}
obj1 = obj
obj1 == obj //return true

typeof与instanceof

typeof() 可以反映出的类型有:number、string、boolean、undefined、object(null历史遗留问题、object、array、date、RegExp )、 function

这里怎么记:基本类型中 的null 叛变 被识别成 object,引用类型中 function 被识别function,而不是object,其他的都按照数据类型来

1
2
3
4
5
 function test(){
console.log(typeof a) //return 字符串的'undefined',返回为真
console.log(a) //直接报错,因为a没有被定义
}
test()

instanceof:用来判断某个构造函数的 prototype 属性所指向的对象是否存在于另外一个要检测对象的原型链上

1
2
3
4
({}) instanceof Object              // true
([]) instanceof Array // true
(/aa/g) instanceof RegExp // true
(function(){}) instanceof Function // true

加减乘除

任何数据类型 + 字符串 都是字符串

NaN,not a number,非数,是一个数字类型,用于一些没有意义的情况,NaN与包括自己在内任何东西都不相等。Number(undefined)、Number(null) 得到的都是NaN

< > <= >= === != !==

  1. 数字和数字比,比大小;
  2. 字符串数字和数字,统一转换成数字进行比较
  3. 非纯数字字符串和数字进行比较,非纯数字字符串会转换为NaN,当NaN和数字比较时不论大小都返回false.
  4. 数字字符串和数字字符串,从第一位开始依次比较ASCII码;
  5. 字符串和字符串,从第一位开始依次比较ASCII码;

逻辑运算

undefined、null、NaN、“”、0、false,除上述以外全部都是真

console.log(1 && 2 && undefined && 10)

  • return undefined
  • 遇到真就往后走,遇到假或走到最后就返回当前的值

console.log(1 || 2 || undefined || 10)

  • return 1
  • 遇到假就往后走,遇到真或走到最后就返回当前的值

类型转换

显式类型转换

  • Number,转数字类型;String,转字符串;Boolean(),转布尔
  • parseInt,从前往后看整数,转整数类型(向下取整);parseFloat,转浮点类型(四舍五入); toFixed(2),取小数点两位
1
2
3
4
parseInt('123a')  return 123
parseInt('12a123') return 12

"89" > '9' return false 字符串比较大小 从第一位的ASCII码开始

隐式类型转换

  • === 不进行隐式转换
  • NaN == NaN,返回 false,NaN不等于任何东西
  • undefined 不大于零 不等于零 不小于零;null 不大于零 不等于零 不小于零;故 undefined == null ,return true
  • isNaN有隐式类型转换,new Number()
1
2
3
4
isNaN('123')  return false
undefined == null return true
undefined === null return false
parseInt('123a') == null return false

函数

函数在传递参数时,多传递的参数会被忽视,少传递的参数会被视为 undefined

实参和形参存储在不同的地方,数量匹配的 会存在一一对应的绑定关系;不匹配的,多传递或少传递的,不会有绑定关系

  • 实参和形参在取值时,默认取 不是undefined的值
  • 每一个函数的形参都是函数的临时变量

函数本身是有length的,test.length 返回实参的长度;函数默认返回值undefined

如何进行实参求和

1
2
3
4
5
6
7
8
9
function sum(){
var a = 0;
for(var i = 0; i < arguments.length; i++){
​ a += arguments[i]
​ }
return a
}

sum(1,2,3,4,5,6,7,8, .....)

实参是可被修改的

1
2
3
4
5
6
funtion a(x,y,z){
z = 10;
console.log(arguments[2])
}
a(1,2,3)
// return 10 reason: undefined -> 3 -> 10

对象

三种声明方式,var arr = {} 对象字面量;var arr = new Object() ;var arr = Object() ; 所有对象都继承于 Object.prototype

对象里的函数 叫 方法 method;在对象中,this就指代对象主体

1
2
3
4
5
6
7
8
var teacher = {
name: 'cyc',
weight: 130,
eat: function(){
this.weight--
}
}
teacher.name teacher['name'] 两个效果一样

数组

三种声明方式,var arr = [] 数组字面量;var arr = new Array() 不推荐;var arr = Array() 用得少; 所有数组都继承于 Array.prototype

数组内最后一位如果是 逗号, 系统会忽略,其余位置的逗号会认为 是empty元素

修改原数组的方法

修改原数组的方法,push、unshift,pop,shift,splice,sort

数组尾部 push 加 pop减

数组头部 unshift加 shift减

push在最后加元素,unshift在最前面加元素,这两个方法都是继承Array原型的方法,返回值 是执行方法之后的数组长度

pop 方法 剪切数组最后一位 并返回出来;shift方法 剪切数组第一位 并返回出来

reverse方法 ,倒序数组

splice方法:arr.splice(开始项的下标,剪切长度,在剪切后的位置添加新数据),返回被剪切的数组,原数组做新数据添加

sort,返回排序后的数组结果,可以通过设置系统内置方法(a 与 b进行比较,小于就不动,大于就交换位置)来达到不同的排序效果,默认按照ASCII码进行升序排序

1
2
3
默认按照ASCII码进行升序排序
var arr = [27, 49, 5, 7]
console.log(arr.sort()) //return [27, 49, 5, 7]
push方法的实现原理
1
2
3
4
Array.prototype.push = function(elem){
this[this.length] = elem;
this.length ++;
}
如何让数组按照数字大小进行排序
1
2
3
4
5
6
7
arr.sort(function(a, b){
return a-b; // 返回升序排序
return b-a; // 返回降序排序

var rand = Math.random();
return rand-0.5; // 打乱一个数组
}

如何让数组按照根据字符串长度排序

1
2
3
4
5
6
7
8
var arr = ['12312','123','1231','1231231']
arr.sort(function(a,b){
if(a.length > b.length){
return 1;
} else{
return -1;
}
})

新建一个数组的方法

concat,toString(数组转字符串),slice

  • arr.slice(开始项的下标,截止项的下标),要区分开slice和splice
  • arr.join(),把数组内的元素取出来用参数进行连接,不填参数默认用,逗号隔开
  • arr.split(‘分割参数‘ ,数组长度),把字符串用分割参数分割后放入数组,和join对应使用

slice和splice的区分

  1. slice 翻译 片段 ;splice 翻译 拼接
  2. 两者功能类似,slice 两参数,起点和止点;splice三参数,起点 长度 和新加数据
  3. 返回值都是被剪切的数组, splice多了一个向原数组添加数据的功能
  4. slice 不修改原数组,splice 修改原数组

ES5新增方法

5个迭代方法(循环操作数组中的各个项):forEach(),map(),filter(),every()和some()
2个归并方法(迭代数组所有项,最终返回一个值):reduce()和reduceRight()
2个索引方法:indexOf() 和 lastIndexOf();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// forEach: 遍历数组,无返回值,不改变原数组
// e 当前元素 ; index当前元素索引值 ; array整个数组,第二个参数改变this指向
.forEach(function(e,index,arr){})

// map: 返回一个新数组,不会改变原数组
// 新数组的元素值是每次函数return的返回值 ; 不写return,接收的新数组的元素值将全为空
var b = [1,2,3].map(function(x){
return x*x; //b得值是[1,4,9]
});

// filter: 过滤不符合条件的元素,保留true,过滤false,返回一个新数组,不改变原数组
var brr = [1,2,3].filter(function(item)){
return item%2; //返回值为奇数的元素
}

//some: 判断数组内是否有指定元素,如果只要有一个则返回true,如果一个都没有则返回false
var brr = [20,13,11,8,0,11].some(function(item){
return item>10;
}) // true

// every: 判断数组内是否都有指定元素,如果全部都有则返回true
var brr = [20,13,11,8,0,11].every(function(item){
return item>10;
}) // false

reduce: 为数组中的每一个元素依次执行回调函数

回调函数四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,原始数组

  • 如果没有提供initialValue,reduce 从索引1的地方开始执行 ,跳过第一个索引
  • 如果提供initialValue,从索引0开始
  • 空数组使用reduce不提供初始值,会报错,因为没有索引1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var arr = [1, 2, 3, 4];
var sum = arr.reduce(function(prev, cur, index, arr) {
console.log(prev, cur, index);
return prev + cur;
})
1 2 1
3 3 2
6 4 3

var arr = [1, 2, 3, 4];
var sum = arr.reduce(function(prev, cur, index, arr) {
console.log(prev, cur, index);
return prev + cur;
},0)

0 1 0
1 2 1
3 3 2
6 4 3

reduceRight()
reduceRight()和reduce()类似,只是执行方向不同,reduceRight()从数组的末尾往前递归

indexOf()
跟字符串查找一样,查找指定元素是否存在,如果存在,返回下标,如果不存在返回-1

lastIndexOf()
lastIndexOf()是从最后一个查找,如果存在,返回下标,如果不存在返回-1

类数组

  • 类数组是结构与数组十分相似,但没有数组那么丰富的内建方法,通常类数组可能还拥有一些别的属性。
  • 类数组一定要有 数组形式下标 的值 和 length属性,length属性值为数组形式下标值数量,其他的属性数量不计入
  • JavaScript 常见的类数组:arguments,类数组的原型继承于Object,不继承Array,所以很多array原生方法用不了,需要额外添加
1
2
3
4
5
6
7
8
9
var obj = {
'2':3,
'3':4,
'length':2,
'push':Array.prototype.push
}
obj.push(1);
obj.push(2); // 返回4 因为obj.length为4
console.log(obj) // return {2: 1,3: 2,length: 4,push: f...} 结合push的实现方法来思考

进阶知识

预编译

JavaScript是解释性语言,也就是说,编译一行,执行一行,但js并非上来就进入编译环节,它在编译之前存在预编译过程。js中预编译一般分为 全局的预编译 和 函数的预编译

预编译过程中 只看 有无变量声明和函数声明,最后再执行(if,while都算执行里)

GO global object,全局上下文 的预编译流程

  1. 找变量声明,初始都为undefined
  2. 找函数声明
  1. 执行 -> 该赋值就赋值 该执行就执行

AO 函数上下文的预编译流程

  1. 寻找形参和变量声明,初始都为undefined,this 指向window
  2. 实参值赋值给形参
  3. 找函数声明、赋值
  4. 执行

值得注意:

  • 暗示全局变量 imply global,任何变量如果变量未经声明赋值,此变量就为全局对象(window)所有;window是一个全局作用域对象
  • 函数未被实例化前,this指向window,this.a=1 实际上为 a=1,都保存在全局中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var x = 1;
y = z = 0;

function add(n){
return n += 1
}
y = add(x);

function add(n){
return n += 3
}
z = add(x);

console.log(x,y,z);
// return 1,4,4 reason: 预编译过程中add函数会被后面的覆盖

作用域和作用域链

作用域:限定这个变量的可用性的代码范围就是这个变量的作用域。

作用域链:上下文代码执行的时候,创建变量对象的一个作用域链,决定了各级上下文代码访问变量和函数时的顺序。代码正在执行的上下文变量始终在作用域链最前端,全局上下文的变量对象始终是作用域链最后一个变量对象。

作用域链(scope chain):由多个作用域对象连续引用形成的链式结构。

函数在被定义的时候,生成自己的作用域和作用域链[[scope]],放入自己的GO;在被执行的时候,生成AO;自己的AO排在GO前面

闭包

闭包指的是 那些引用了另一个函数作用域变量的函数,通常在嵌套函数中实现。return 返回函数的时候,会将当前AO抛出去,变成GO里的全局变量,从而产生闭包,

  • 当内部函数被返回到外部并保存时,一定会产生闭包
  • window. = function(…) window也可以产生闭包的效果

用闭包模拟私有方法:产生闭包后,在当前AO定义的变量,称为该函数的私有变量,其他地方不能访问

坏处:闭包会产生原来的作用域链不释放,过度的闭包可能会导致内存泄漏或加载过慢

立即执行函数

IIFE,immediately-invoked function,也可被称为初始化函数

  • 立即自动执行执行一次完成后立即释放独立作用域,常被用于开发模块
  • 立即执行函数 + 构造函数 + window闭包,非常适合写js插件

写法如下: 匿名函数+执行小括号;括号包起来的任何东西都变成表达式

1
2
3
;(function(a,b){
return a+b
})(1,2);

不给函数后加括号就不是执行函数!一定要注意这个细节

1
2
3
4
5
function test(x){
console.log(arguments);
return x;
}(1,2,3,4,5)
// return 5

函数声明变成表达式的方法

  • 函数变成表达式后,函数名即被忽略;一定是表达式 才能被执行符号()执行
  • 在函数前加上 + - ! || && 等可以让函数变成表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function test(){
var arr = []
for( i = 0; i<10; i++){
arr[i] = function(){
console.log(i)
}
}
return arr;
}

var myArr = test();
for(var j = 0; j<10; j++){
myArr[j]();
}
// return 打印出来 10个10 reason: i<10时,function里面的内容都被忽略,i=10时,arr被返回出全局,产生闭包,此时的i为全局变量,且等于10,故打印的全部都是10

逗号分隔符

逗号分隔符, 返回最后一个值

1
2
3
var num = (1,2,3,4,5)
console.log(num)
// return 5
1
2
3
4
5
6
7
8
9
10
var fn= (
function test1(){
return 1;
},
function test2(){
return '2';
}
);
console.log(typeof(fn));
// return function
1
2
3
4
5
6
7
8
9
10
var fn= (
function test1(){
return 1;
},
function test2(){
return '2';
}
)();
console.log(typeof(fn));
// return string
1
2
3
4
5
6
var a = 10
if(function b(){}){
a += typeof(b);
}
console.log(a)
// return 10undefined reason:(function b(){})通过,且忽视函数名,b未被定义,即undefined

构造函数

构造函数在实例化之前,构造函数就是一个函数。构造函数上的方法指向构造函数,并不指向实例对象;构造函数原型上的方法指向实例本身

函数默认返回值是undefined,构造函数实例化后默认返回值是this,this后跟函数会产生隐式闭包

实例化后,原本指向window的this 指向 一个实例化新对象

1
2
3
4
5
AO = {
this: {
__proto__: 函数名.prototype
}
}

实例化后,相当于创建了一个对象object,this指向这个实例化对象,不指向构造函数

1
2
3
4
5
6
7
8
9
10
11
function Teacher(opt){
this.name = opt.name;
this.eat = function(){
...
// 隐式做了这件事情,实例化后产生一个全局闭包
// return this
}
}
var teacher = new Teacher({
name:'123'
});

class 与 构造函数的关系

通过class定义的类 和通过构造函数定义的类 二者本质相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person{
constructor (name) {
this.name = name
}
say () {
console.log('hello')
}
}

function Person(name){
this.name = name,
Person.prototype.say = function() {
console.log('hello')
}
}

包装类

为了便于操作基本类型值,JS 提供了三种特殊的引用类型:Boolean、Number、String(undefined和null没有自己的包装类)

每当读取一个基本类型值的时候,引擎就会创建一个对应的基本包装类型的对象”,能够调用一些方法来操作这些数据。

包装类 只能访问,不能做保存,如果需要保存,就使用new实例化生成对象

1
2
3
4
5
6
7
8
9
var name = '123123123'   
name += 10; //12312312310

var type = typeof(name); //string
if(type.length === 6){ //true
type.text = 'string' // new String(type).text = 'string' delete
}
console.log(type.text) // undefined
// return undefined reason: 包装类 只能访问,不能做保存

原型与原型链

constructor —> 构造函数;prototype —> 原型;_proto_ —> 原型容器

prototype 是属于每个实例化对象,并不属于构造函数;_proto_ 是每个实例化对象的原型的容器

JavaScript原型链是一种用于实现面向对象编程的设计,它让JavaScript对象可以继承其他对象的属性和方法。JavaScript原型链是一种指针链,每个对象都有一个内置的属性,叫做原型(prototype),它指向该对象的父对象(即创建该对象的对象)。原型也是一个对象,所以它也有自己的原型,这样就形成了一个原型链。原型链的终点是一个叫做Object.prototype的对象,它是所有对象的默认原型。Object.prototype的原型是null,表示原型链的结束

  • 原型链的问题:原型中包含的引用值会在所有实例中共享,所以将写死的值和方法 写到 prototype,需要传参的 写到构造函数里,传参值的优先级高于prototype

  • 如何修改原型链:子类可以修改父类的 引用值,不能修改父类的原始值;子类虽然不能修改父类的原始值,但是可以copy一份父类原始值后再修改自己。实例化生成的对象一般无法对 原型 进行 增加改, 通过 ._proto_ 这种方式可以

  • 原型链顶端 : Object.prototype,不是Object

  • 自定义原型:Object.create( 填对象或者null) ,可以自定义原型 prototype

call/apply

  • call/apply可以更改 构造函数的this指向,只能借用指定的属性,不能继承上级全部的属性和方法

  • 两者区别是 传参不同,call(this, 1, 2, 3, …) ; apply(this, [1,2,3,…])

  • 通过 call/apply,可以使用属于另一个对象的方法

1
2
3
4
5
6
7
8
9
10
11
var person = {
fullName: function(city, country) {
return this.firstName + " " + this.lastName + "," + city + "," + country;
}
}
var person1 = {
firstName:"Bill",
lastName: "Gates"
}
// person1 可以使用 person.fullName 方法
person.fullName.call(person1, "Seattle", "USA");

eval

eval() 函数计算或执行参数,如果参数是表达式,则 eval() 计算表达式。如果参数是一个或多个 JavaScript 语句,则 eval() 执行这些语句

1
2
3
4
5
6
7
var x = 10;
var y = 20;
var a = eval("x * y") + "<br>";
var b = eval("2 + 2") + "<br>";
var c = eval("x + 17") + "<br>";

var res = a + b + c; // return 200 4 27

垃圾回收

JavaScript垃圾回收 是一种自动内存管理机制,可以监控内存分配。垃圾回收的目的是防止内存溢出,提高程序的性能和稳定性。

垃圾回收是一个周期性的过程,在一定的时间间隔内执行。

基本原理是从全局对象开始,找出所有引用的对象,再找这些对象的引用对象,以此类推,形成一个可达对象的集合。 标记所有不可达对象,释放内存空间。这种算法被称为 标记-清除。

疑难点

如何认识new

new 申请内存, 创建对象,当调用new时,后台会隐式执行new Object()创建对象。所以,通过new创建的字符串、数字是引用类型,而是非值类型。

1
2
3
let value = '25';
let obj = new Number(value);
conosole.log(typeof obj) // return object

检查数据类型的方法

typeof

typeof() 可以反映出的类型有:number、string、boolean、undefined、object(null历史遗留问题、object、array、date、RegExp )、 function

instanceof

instanceof可以正确判断对象的类型,其内部运行机制是判断在其原型链中能否找到该类型的原型

instanceof只能正确判断引用数据类型,而不能判断基本数据类型。因为基本数据类型没有原型链

1
2
3
4
5
(function(){}) instanceof Function  // true
(function(){}) instanceof Object // true

console.log(2 instanceof Number); // false
console.log(true instanceof Boolean); // false

constructor

constructor有两个作用,一是判断数据的类型,二是对象实例通过 constrcutor 对象访问它的构造函数。

1
2
3
4
5
6
console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true
console.log(({}).constructor === Object); // true

需要注意,如果创建一个对象来改变它的原型,constructor就不能用来判断数据类型了:

1
2
3
4
5
6
7
8
function Fn(){};

Fn.prototype = new Array();

var f = new Fn();

console.log(f.constructor===Fn); // false
console.log(f.constructor===Array); // true

Object.prototype.toString.call()

使用 Object 对象的原型方法 toString 来判断数据类型(Object上原型toString方法(返回对象的具体类型)):

A instanceof B,A对象的原型链上到底有无B原型;第三种是最实用的

1
2
3
var a = []

Object.prototype.toString.call(a); return [object Array]

同样是检测对象obj调用toString方法,obj.toString()的结果和Object.prototype.toString.call(obj)的结果不一样,这是为什么?

这是因为toString是Object的原型方法,而Array、function等类型作为Object的实例,都重写了toString方法。不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法(function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串…),而不会去调用Object上原型toString方法(返回对象的具体类型)

采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object原型上的toString方法。

技巧用法

插件的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ES5的写法
;(function(){
var Test = function(){

}
Test.prototype = {

}
window.Test = Test
})();

ES6的写法
class name{
constructor(){
//构造方法
}
methods(){
//原型方法
}
init(){
//初始化函数
}
}

链式操作

方法里每次最后都return this,可以达到连续调用的效果

1
2
3
4
5
6
7
8
9
10
11
var sched = {
morning: function(){
...
return this
},
evening: function(){
...
return this
}
}
sched.morning().evening()

圣杯模式

原型并不可以随便赋值,因为原型是引用对象,随便赋值会导致 子类轻易修改父类原型,如果需要 引用父类原型加以修改 且不产生影响,则可以引入第三个空构造函数实例化后做中转使用

1
2
3
4
5
6
7
8
9
10
11
12
//企业级写法!不会污染全局变量  Origin被继承 Target欲继承 
var inherit = (function(Target,Origin){
function Buffer(){}
return function(Target,Origin){
Buffer.prototype = Origin.prototype;
Target.prototype = new Buffer();
//还原构造器
Target.prototype.constructor = Target;
//说明继承源
Target.prototype.super_class = Origin;
}
})();

克隆/浅拷贝/深拷贝

  • 克隆,只拷贝的地址,var a = b

  • 浅拷贝,只拷贝第一层属性信息,深层信息如一个变,其他跟着变

    对于引用值,浅复制仅仅复制引用值的存放地址,而不复制实际的存放对象。

1
2
3
4
5
6
7
8
9
function clone(origin, target){
var tar = target || {};
for (var key in origin){
if (origin.hasOwnProperty(key)){
tar[key] = origin[key]
}
}
return tar;
}
  • 深拷贝,全部完整的拷贝下来,深复制把要复制的对象所引用的对象都复制了一遍
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function deepClone(origin, target){
var target = target || {},
toStr = Object.prototype.toString,
arrType = '[object Array]';

for (var key in origin){
if (origin.hasOwnProperty(key)){
if(typeof(origin[key] === 'object' && origin[key] !== null){
toStr.call(origin[key]) === arrType ? target[key] = [] : target[key] = {};
deepClone(origin[key], target[key]);
} else {
target[key] = origin[key];
})
}
}
return tar;
}

this指向

this 是JavaScript关键字,是当前环境执行期上下文对象的一个属性,在不同环境下表现不同。一般原则:谁调用this的宿主,this就指向谁

全局this

全局作用域下的this 和window的关系是相同的,不同环境下的全局对象获取方式并不相同

  • web:window、self、frames、this
  • node:global ; web workers:self
  • 通用获取方式:globalThis (Web Worker为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程)

严格模式下,谁调用函数,函数内部的执行默认就是谁

1
console.log(this === window ) // return true

apply 、 call 和 bind 调用模式

call、apply是立即执行、bind是返回一个新的函数

1
2
// test.bind 只会生效一次,下式等同于  t = test.bind(obj)
t = test.bind(obj).bind(obj2)

箭头函数

箭头函数 不是构造器,不能new。 箭头函数是静态this指向,忽略任何形式(call、apply、bind)的this指向改变

箭头函数中 this 找上层函数(非箭头)的作用域,不是谁绑定就指向谁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
'use strict';
const test = () =>{
console.log(this)
}
function test1(){
console.log(this)
}
test(); // return window 箭头函数严格模式下this也指向window
test1(); // return undefined 一般函数严格模式下this指向undefined

var obj = {
a:3,
b:4,
test1: function(){
console.log(this.a) // return 3,但是会被call等方法改变this指向
},
test2: () => {
console.log(this.a) // return undefined test2没有上层非箭头函数,所以指向全局window
},
test3: function(){
var s = () => console.log(this.a)
return s // 永远return 3
}
}

对象

对象方法内部的this 指向 最近一层引用

1
2
3
4
5
6
7
8
9
10
11
var obj = {
a: 1,
test: function(){
console.log(this.a); // return 1
function t(){
// t函数 最近的引用并不是 obj,不属于任何容器
console.log(this.a); // return undefined
}
t()
}
}

defineProperty

this指向调用对象

函数 和 构造函数

构造函数里 默认隐式返回this,如果手动返回了一个新对象,this指向的对象就被忽略了(不生效)

如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象

1
2
3
4
5
function Test(){
console.log(this)
}
Test() // return window
new Test() // return Test{}

事件处理函数

事件处理函数内部的this总是指向被绑定的DOM元素,比如 谁调用onclick,函数就绑定哪个元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
;(function(doc){
var oBtn = doc.getElementById('..');
function Plus(a, b){
this.a = a;
this.b = b;

this.init();
}
Plus.prototype.init = function (){
this.bindEvent();
}
// 第1种情况,会出问题
Plus.prototype.bindEvent = function (){
oBtn.addEventListener('click',this.handleBtnClick,false);
}

Plus.prototype.handleBtnClick = function (){
console.log(this) // return <button....>
console.log(this.a,this.b); // return undefined undefined
}

// 第2种情况,正确
Plus.prototype.bindEvent = function (){
//把plus实例绑入handleBtnClick中
oBtn.addEventListener('click',this.handleBtnClick.bind(this),false);
}

Plus.prototype.handleBtnClick = function (){
console.log(this.a,this.b); // return 3 4
}

// 第3种情况,正确
Plus.prototype.bindEvent = function (){
var _self = this;
//把plus实例绑入handleBtnClick中
oBtn.addEventListener('click',_self.handleBtnClick(),false);
}

Plus.prototype.handleBtnClick = function (){
console.log(this.a,this.b); // return 3 4
}

window.Plus = Plus;

})(document)

new Plus(3,4)

重要实例

1
2
3
4
5
6
7
8
9
10
11
var foo = "123"
function print(){
var foo = "456"
this.foo = "789"//此时并没有对象调用函数print(),所以这里更改的的windoe的GO里面的foo值
console.log(foo)// 456 此时打印的是自己AO里面的foo值!
}

console.log(foo)// 123
print()
console.log(foo)// 789

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var name = '222';
var a = {
name : '111',
say: function(){
console.log(this.name);
}
}
var fun = a.say; // var fun = function(){ console.log(this.name);}
fun(); // 222
a.say(); // 111
var b = {
name: '333',
say: function(fun){
fun();
}
}
b.say(a.say); // 222 这个地方要多注意! 将a.say,也就是函数作为实参传入到对象b内的say()函数,fun()在函数内部也是自己调用,没有对象调用,此时this仍然指向window
b.say = a.say;
b.say() // 333
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function Foo(){
getName = function(){
console.log(1);
}
return this;
}
Foo.getName = function(){
console.log(2);
}
Foo.prototype.getName = function(){
console.log(3);
}
var getName = function(){
console.log(4);
}
function getName(){
console.log(5);
}

Foo.getName(); // 2 Foo.getName()理解为Foo对象的一个属性,并不涉及Foo内部
getName(); // 4 undefined -> console.log(5); -> console.log(4);
Foo().getName(); // 1 Foo()执行后,内部的getName被提升至全局,全局的console.log(4) -> console.log(1)
getName(); // 1 Foo()执行后,内部的getName被提升至全局,全局的console.log(4) -> console.log(1)
new Foo.getName(); // 2 Foo.getName()理解为Foo对象的一个属性,并不涉及Foo内部
new Foo().getName(); // 3 实例化Foo()后,找Foo内部的this.getName,但是找不到,因为getName是window的,只能去原型里找,故返回3
new new Foo().getName(); // 3 原因同上

defineProperty

Object.defineProperty() 方法直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象。

1
Object.defineProperty(obj, prop, descriptor)
  • obj 需要定义属性的对象。
  • prop 需被定义或修改的属性名。
  • descriptor 需被定义或修改的属性的描述符。

数据描述符和存取描述符均具有以下可选键值:

  • configurable: 仅当该属性的 configurable 为 true 时,该属性才能够被改变,也能够被删除。默认为 false
  • enumerable: 仅当该属性的 enumerable 为 true 时,该属性才能够出现在对象的枚举属性中。默认为 false
  • value: 该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined
  • writable: 仅当仅当该属性的writable为 true 时,该属性才能被赋值运算符改变。默认为 false
  • get: 一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。该方法返回值被用作属性值。undefined
  • set: 一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为undefined

callee 和 caller

caller 直接翻译为调用者,callee 翻译为被召者

  • arguments.callee返回当前正执行的函数

    • 适用于自启动函数中递归

      1
      2
      3
      4
      5
      6
      var sum =(function(n){
      if(n<1){
      return 1;
      }
      return n + arguments.callee(n-1)
      })(10);
  • caller 指向调用当前函数的函数。

    1
    2
    3
    4
    5
    6
    7
    function myq() {
    if (myq.caller === null) {
    console.log("该函数在全局作用域内被调用!");
    } else console.log("调用我的是函数是" + myq.caller);
    }
    myq();
    // return 该函数在全局作用域内被调用!
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function myq() {
    if (myq.caller == null) {
    console.log("该函数在全局作用域内被调用!");
    } else console.log("调用我的是函数是" + myq.caller);
    }
    function callQ() {
    myq();
    }
    callQ();
    /* return
    调用我的是函数是function callQ() {
    myq()
    }
    */

DOM 文档对象模型

DOM:document object model 文档对象模型,通过浏览器提供的这一套方法去表示和操作HTML和XML,XML(标签自定义) –> XHTML –> HTML(标签由浏览器定义)

js本身无法直接操作CSS样式的,但是可以通过设置元素的style内联样式,达到覆盖原样式生成新样式的效果

document方法

document 是整个html文档的父级节点,html文档没有父级元素;document 的父级元素规定为null,document 实质上是一个对象

  • document下属get方法一般获取的是 类数组
  • 在IE8及以下,document下属方法参数不区分大小写,要注意兼容性!
  • id属性一般对接后端使用,能不用就不用

常用的几个方法:

  • document.getElementsByTagName ; document.getElementsByClassName,不兼容IE8及以下 ;querySelector、querySelectorAll,兼容IE7及以上
  • querySelector(),返回第一个符合的;querySelectorAll(),返回满足条件的类数组。一般不使用,因为性能不好,和不实时性

DOM结构树

  • Document下属HTMLDocument和XMLDocument,Element同理
  • 节点不是元素,节点包含元素,元素的名字为元素节点,也可被称为DOM元素;document的原型是 HTMLDocument,HTMLDocument的原型是 Document

image-20221012215124687

节点分类和属性

  • 元素节点 —> 1 ; 属性节点 —> 2 ; 文本节点 —> 3,换行也算文本节点

    注释节点 —> 8 ; document —> 9 ;DocumentFragment —> 11

  • nodeName,只读不可改 ; nodeValue,可读可改 ; nodeType,1 2 3 8 9 11; attributes、getAttributeNode、可读可改

获取元素和节点的方法

获取父级

parentNode、parentElement IE9及以下不支持

parentNode 获取包括元素的父级节点,parentElement 只获取父级元素

parentNode w3c规范;parentElement ie规范,在firefox浏览器不能使用

1
2
document.documentElement.parentElement  // return null
document.documentElement.parentNode // return #document

获取子级

childNodes、children IE7及以下不支持

childNodes 获取子级节点,children 获取子级元素

其他获取方式

firstChild、lastChild;firstElementChild、lastElementChild IE9及以下不支持

nextSibling、previousSibling ; nextElementSibling、previousElementSibling IE9及以下不支持 其中区别与上面同理

以上方法注意点

  1. getElementById()、getElementByName这两个方法,只有Dodument有,元素节点使用该方法会报错
  2. getElementByTagName、getElementByClassName、querySelector、querySelectorAll 这四个方法Document和Element原型里都有
  3. document.documentElement 获取 HTML元素
  4. var div = document.getElementById(‘div’)[0],经历了两个过程,选元素,再实例化
  5. document.title 获取的是 title值,不是title元素

body和head的两种获取方式

  • var body = document.getElementsByTagName(‘body’)[0]
  • 特殊的常用方式:var body = document.body ;

有关节点增删改

创建节点

appendChild 这是个节点 node方法,在父级元素的最下边添加,类似于push,appendChild 不是复制节点,而是剪切节点

1
2
var div = document.createElement('div'); //在堆内存里创建一个div对象,
document.body.appendChild(div) // 在节点树中添加该元素

插入节点

c.insertBefore(a, b),在父级c节点下的子节点b之前插入a节点

删除节点

  • 父删除子,父节点.removeChild(子节点),返回值是子节点;该方法并不是销毁该节点,而是从节点树中取出

  • 子自己删除自己,remove() ,直接销毁自己,返回undefined

节点替换

parent.replaceChild(new, origin)

增加节点内容

innerHTML 可以取值赋值追加值,可以加HTML字符串; innerText 可以取值赋值追加值,不可以加HTML字符串,因为字符实体

元素节点的方法

setAttribute(属性名,属性值) 给节点赋值属性

自定义属性

HTML5 给元素增加了一个data-*属性,两种方式访问

  1. 节点.dataset(*)方法去访问自定义属性
  2. 用getAttribute方法访问时,需要带上前面的data-

创建文档片段

DOM树节点每次有变动时,会引起页面回流,即重新计算几何数据再渲染。当频繁对DOM树进行操作时,极大占用了资源开销,此时就需要用到DocumentFragment

DocumentFragment不在节点树里,可将需要添加的元素节点放到DocumentFragment中,最后统一加到节点树中

浏览器相关的DOM操作

浏览器的怪异模式和标准模式

document.compatMode方法 判断浏览器的模式

  • BackCompat 怪异模式,浏览器厂商开发,向后兼容规范,
  • CSS1Compat 标准模式,w3c指定兼容规范,; <!DOCTYPE html> html 文件第一行代码如果是这个,即标准模式

查看滚动条的距离

image-20221015162934492

多平台兼容性写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
function getScrollOffset(){
if(window.pageXOffset){
return {
left: window.pageXOffset,
top: window.pageYOffset
}
} else {
return {
left: document.body.scrollLeft + document.documentElement.scrollLeft,
top: document.body.scrollTop + document.documentElement.scrollTop
}
}
}

浏览器可视区域

常规:window.innerWidth/ innerHeight

IE9及以下:标准模式:document.documentElement.innerWidth/innerHeight; 怪异模式:document.body.clientWidth/clientHeight

多平台兼容性写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function getViewportSize(){
if(window.innerWidth){
return{
width: window.innerWidth,
height: window.innerHeight
}
} else {
if(document.compatMode === 'BackCompat'){
return{
width: document.body.clientWidth,
height: document.body.clientHeight
}
} else{
return{
width: document.documentElement.clientWidth,
height: document.body.clientHeight
}
}
}
}

整个页面的宽高

多平台兼容性写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
function getScrollSize(){
if(document.body.scrollHeight){
return {
width: document.body.scrollWidth,
height: document.body.scrollHeight
}
} else {
return {
width: document.documentElement.scrollWidth,
height: document.documentElement.scrollHeight
}
}
}

其他问题

  • offsetLeft,offsetTop 找上一个父级定位元素的距离

  • offsetParent,返回上一个父级定位元素

  • 返回元素到左上角的距离(不管是否有定位元素)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function getElementDocPosition(el){
    var parent = el.offsetParent,
    offsetLeft = el.offsetLeft,
    offsetTop = el.offsetTop;
    while(parent){
    offsetLeft += parent.offsetLeft;
    offsetTop += parent.offsetTop;
    parent = parent.offsetParent;
    }
    return {
    left: offsetLeft,
    top: offsetTop
    }
    }

操作滚动条

  • window.scroll(x, y) 和window.scrollTo(x, y) 等效,滚动到绝对位置
  • window.scrollBy(x, y) 滚动相对距离

DOM控制CSS样式

  • css样式表里的信息,控制台是无法访问到的

  • DOM无法直接控制css样式,可以通过 标签style属性进行设置

  • window.getComputedStyle(elem, null)[prop] 查看计算样式,IE8及以下不支持

    获取样式的多平台兼容性写法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function getStyles(elem, prop){
    if(window.getComputedStyle){
    if(prop){
    return window.getComputedStyle(elem,null)[prop];
    } else {
    return window.getComputedStyle(elem,null);
    }
    } else {
    if(prop){
    return elem.currentStyle[prop];
    } else {
    return elem.currentStyle;
    }
    }
    }
  • offsetWidth offsetHeight计算元素宽高,但是算入了padding,在企业中不常用,用上面封装的函数获取宽高会更合适

  • 如何获取伪元素的属性,eg: div:: after{ … }

    • width.getCoumputedStyle(div, ‘after’).width
  • 操作伪元素的最好方法就是 加一个类

事件代理

  • 事件是不需要绑定的,绑定的是事件处理函数

  • 事件冒泡,从子元素开始,触发了 事件,默认会向父级元素传递,可以一直向上级传递,body、html、document、window

  • 事件对象,event,事件触发那一刻所有相关信息和方法的集合,event的原型链如图所示

    image-20221028133521313

  • 阻止事件冒泡的方法:

    • e.stopPropagation() W3C规范

    • e.cancelBubble = true IE

    1
    2
    3
    4
    5
    6
    var e = e || window.event;
    if(e.stopPropagation){
    e.stopPropagation();
    } else{
    e.cancelBubble = true;
    }
  • 事件代理:给父元素绑定事件处理函数,通过事件源对象找到子元素,然后执行特定的程序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var items = document.getElementsByTagName('li');

    listWrapper.onclick = function(e){
    var e = e || window.event,
    tar = e.target || e.srcElement, //事件源对象
    tarName = tar.tagName.toLowerCase();

    if(tarName === 'li'){
    console.log(tar.innerText);
    var index = [].indexOf.call(items, tar);//获取索引的方法
    console.log(index);
    }
    }

  • 事件流的三个阶段

    • 捕获阶段:从window对象传导到目标节点(上层传到底层)称为“捕获阶段”(capture phase),捕获阶段默认不执行处理函数;
    • 目标阶段:在目标节点上触发,称为“目标阶段”
    • 冒泡阶段:从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段”(bubbling phase)。事件代理即是利用事件冒泡的机制把里层所需要响应的事件绑定到外层;
    1
    2
    window.addEventListener('click',function(){},false)
    //第三个参数 false 捕获阶段默认不执行; true 捕获阶段默认执行

日期对象 Date()

  • getDate() 返回当前是一个月的第几天 1-31
  • getDay() 返回当前是一周中的第几天 0-6 周日算0
  • getMonth() 返回当前为第几个月, 0-11 记得要加1
  • getFullYear() 返回年份、getHours、getMinutes、getSeconds
  • getTime,获取时间戳,自此1970年1月1日0点0分0秒后过了多少毫秒
  • 以上函数只是记录片段,并不是实时计算,当算持续时间的时候,需要重新实例化一次
  • setInterval 、setTimeout 默认归属于window
  • Math.round() 并不是完全的四舍五入,在负的点5的时候,退一位 eg:Math.round(-5.5) return -5

计时器

setInterval ,间隔时间重复执行;clearInterval 取消计时器

  • setInterval 运行过程中只取一次值,无法更改
  • setInterval 的返回值是它的唯一标识(从1开始),即多次实例化后的打印输入会累加
  • clearInterval() 参数是 Interval 的唯一标识
1
2
3
4
5
6
setInterval(function(){

},1000) // 参数1,执行的函数;参数2,间隔多少毫秒执行一次

var test(){};
setInterval(test,1000) // 这里可以写成‘test()',但是不可以直接写test()

延迟器

setTimeout,延迟特定的毫秒时间执行一次函数;clearTimeout,取消延迟器

参考资料

(8条消息) JavaScript 中堆和栈的区别_you_wne的博客-CSDN博客_js堆和栈的区别