前端三件套知识点小结

前端入门

开发工具:浏览器(Chrom、FireFox、Edge、Safari)
编辑器:VSCode

HTML

1.HTML

HTML:超文本标记语言 ( HTML ) 是用来构建你的网页内容并将其语义化的代码。
HTML5:HTML5指的是包括 HTML 、 CSS 和 JavaScript 在内的一套技术组合。
HTML5元素定义:
  • 结构性元素:负责web上下文的定义,section、header、footer、nav、article
  • 块级性元素:完成web页面区域的划分,确保内容的有效分割,aside、figure
  • 行内语义元素:完成web页面具体内容的引用和描述
  • 交互性元素:主要用于功能性的内容表达,会有一定的内容和数据的关联,是各种事件的基础

2.标签

radio:在使用radio标签时,为了只能单选,需要将radio标签的name属性对应的值相同
label:使用label标签绑定元素时,当点击label便签内的文本时,浏览器就会自动聚焦或者选择对应的表单元素上
性别:<label for="man">
            <input type="radio" name="sex" id="man">label>
        <label for="woman">
            <input type="radio" name="sex" id="woman">label>
锚点链接:为我们点击链接,可以快速定位到页面中的某个位置
  • 在链接文本的href属性中,设置属性值为#名字的形式
  • 找到目标位置标签,里面添加一个id属性=刚才的名字
<a href="#two">第二集a>
<h3 id="two">第二集内容h3>
select下拉菜单:有多个选项供用户选择
<select>
    
    <option select="selected">选项一option>
    <option>选项二option>
    <option>选项三option>
select>

CSS

1.CSS

css:层叠样式表,用于设计风格和布局
css三个重要特性:
  • 层叠性:相同选择器给设置相同的样式,此时一个样式就会覆盖另一个冲突的样式。样式冲突,执行就近原则,样式不冲突,不会重叠
  • 继承性:子标签会继承父标签的某些样式,如字体样式等
  • 优先级:当一个元素指定多个选择器,就会有优先级的产生,选择器不同,则根据选择器的权重执行,权重值大小:!important>行内样式>ID选择器>类选择器>元素选择器>继承

2.选择器

  • 类型选择器:也叫做"标签名选择器"或者是"元素选择器",直接为标签加入样式
  • 类选择器:以一个句点(.)开头,会选择文档中应用了这个类的所有物件,你能对一个元素应用多个类,然后分别指向它们,或者仅仅在选择器中存在所有这些类的时候选择这一元素
  • ID选择器:ID选择器开头为#而非句点,不过基本上和类选择器是同种用法
  • 伪类选择器:伪类是选择器的一种,它用于选择处于特定状态的元素,比如当它们是这一类型的第一个元素时,或者是当鼠标指针悬浮在元素上面的时候
  • 关系选择器:典型用单个空格字符——组合两个选择器,比如,第二个选择器匹配的元素被选择,如果他们有一个祖先(父亲,父亲的父亲,父亲的父亲的父亲,等等)元素匹配第一个选择器

span {
    background-color: yellow;
}
<span>Type selectorsspan>


.highlight {
    background-color: yellow;
}
<h1 class="highlight">Class selectorsh1>


h1#heading {
    color: rebeccapurple;
}
<h1 id="heading">ID selectorh1>



li[class] {
    font-size: 200%;
}

li[class="a"] {
    background-color: yellow;
}

li[class~="a"] {
    color: red;
}

<h1>Attribute presence and value selectorsh1>
<ul>
    <li>Item 1li>
    <li class="a">Item 2li>
    <li class="a b">Item 3li>
    <li class="ab">Item 4li>
ul>


.box::before {
    content: "This should show before the other content."
}   
<p class="box">Content in the box in my HTML page.p>


.box p {
    color: red;
}  
<div class="box">
    <p>Text in .boxp>
div>

3.文本样式

font:复合字体样式属性,用于定义字体、大小、粗细和文字样式,必须保留font-size和font-family属性,否则font属性将不起作用
body{
	font:font-style font-weight font-size/line-height font-family;
}
text-decoration:给文本添加下划线(underline)、删除线(line-through)、上划线(overline)

4.CSS引入方式

  • 内部样式表:在style标签中书写CSS代码,style标签写在head标签中
  • 行内样式表:使用style属性引入CSS样式
  • 外部样式表:CSS代码保存在扩展名为.css的样式表中,HTML文件引用扩展名为.css的样式表,有两种方式:链接式、导入式。
 
 <h1 style="color:red;">使用行内样式引入CSSh1>


<style type="text/css">
  div{
      background: green;
  }
style>
<div>我是DIVdiv>



<link rel="stylesheet" type="text/css" href="css/style.css" /> 


<style type="text/css">
   @import url("css/style.css");
style>


5.background属性

background-repeat属性用于控制图像的平铺行为。可用的值是:
  • no-repeat:不重复
  • repeat-x:水平重复
  • repeat-y:垂直重复
  • repeat:在两个方向上重复
background-size:设置长度或百分比值,来调整图像的大小以适应背景
  • cover:浏览器将使图像足够大,使它完全覆盖了盒子区,同时仍然保持其高宽比
  • contain:浏览器将使图像的大小适合盒子内
background-position:允许您选择背景图像显示在其应用到的盒子中的位置
  • 两个单独的值:一个水平值后面跟着一个垂直值
  • 使用长度值和百分比
  • 混合使用关键字,长度值以及百分比
background-attachment:设置背景图像是随对象滚动还是固定不动
  • scroll:背景图像随对象滚动而滚动,是默认选项
  • fixed:背景图像固定在页面不动,只有其他的内容随滚动条滚动
background混合写法:背景颜色 背景图片地址 背景平铺 背景图像滚动 背景图片位置
.box{
	background-image: url(star.png);
  	background-repeat: no-repeat;
 	background-position: top 20px right 10px;
	background-position: top center;
}

6.盒子模型

盒子模型:CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:边距,边框,填充,和实际内容。
  • 标准盒模型(border-box):一个块的总宽度=width+margin(左右)+padding(左右)+border(左右)
  • 怪异盒模型(content-box):一个块的总宽度=width+margin(左右)(既width已经包含了padding和border值)
padding:如果盒子本身没有width/padding属性,加入padding则不会撑开盒子
margin:外边距可以让块级元素水平居中,但是必须满足两个条件:
  • 盒子必须指定了宽度width
  • 盒子左右的外边距都设置为auto:margin:0 auto
外边距合并:使用margin定义块元素的垂直外边距时,可能会出现外边距的合并
清除浮动:父元素高度自适应,子元素float后,造成父元素高度为0,造成高度塌陷
  • 由于浮动元素不再占用原文档流的位置,所以它会对后面的元素排版产生影响
清除浮动本质
  • 清除浮动元素造成的影响
  • 如果父盒子本身有高度,则不需要清除浮动
  • 清除浮动之后,父级元素就会根据浮动的子盒子自动检测高度,就不会影响下面的标准流了

7.定位

相对定位:元素在移动位置的时候,是相对于它原来的位置来说的
  • 相对于自己原来的位置移动
  • 原来在标准流的位置继续占有,后面的盒子仍然以标准流的方式对待(不脱标,继续保留原来位置)
  • position:relative
绝对定位:元素在移动位置的时候,是相对于它的父元素来说的
  • 如果没有祖先元素或者祖先元素没有定位,则以浏览器为准定位
  • 如果祖先元素有定位(相对、绝对、固定),则以最近一级有定位的祖先元素为参考点移动位置
  • 绝对定位不再占有原来的位置(脱标)

8.元素的显示与隐藏

display:属性设置为none隐藏元素后,不再占有原来的位置
overflow:让一个元素在页面中隐藏或者显示出来
  • visible(默认选项):不剪切也不添加滚动条
  • hidden:不显示超过对象尺寸的内容,超出的部分隐藏掉
  • scroll:不管超出与否,总是显示滚动条
  • auto:超出自动显示滚动条,不超出不显示滚动条
vertical-align:用于设置图片或者表单和文字垂直对齐
  • baseline(默认):元素放在父元素的基线上
  • middle:把此元素放置在父元素的中部
单行文本溢出显示省略句:
  • 先强制一行内显示文本:white-space:nowrap
  • 超出的部分隐藏:overflow:hidden
  • 文字用省略号替代超出的部分:text-overflow:ellipsis
多行文本溢出显示省略号:
calc()函数:让你在生命CSS属性值时执行一些计算,calc(100%-50px)
transition:变化的属性 花费时间 运动曲线 合适开始
  • transition:width .5s ease 0s
  • 如果想要多个属性都变化,属性写all即可

CSS转换

转换(transform):可以实现元素的位移、旋转、缩放等效果
  • 移动:translate
  • 旋转:rotate
  • 缩放:scale

Flex布局

flex原理:flex是flexible box的缩写,意为“弹性布局”,用来为盒状模型提供最大的灵活性,任何一个容器都可以指定为flex布局
  • 当我们为父盒子设为flex布局后,子元素的float、clear和vertical align属性将失效
  • 伸缩布局=弹性布局=伸缩盒布局=弹性盒布局=flex布局
flex常见的父项属性(对父元素设置的):
  • flex-direction:设置主轴的方向
  • justify-content:设置主轴上的子元素的排列方式
  • flex-wrap:设置子元素是否换行
  • align-content:设置侧轴上子元素的排列方式(多行)
  • align-items:设置侧轴上的子元素排列方式(单行)
  • flex-flow:复合属性,相当于同时设置了flex-direction和flex-wrap
flex-direction设置主轴的方向:在flex布局中,是分为主轴和侧轴两个方向,同样的叫法有:行和列、x轴和y轴
  • row(从左向右,沿x轴方向)
  • column(从上向下,沿y轴方向)
justify-content:设置主轴上子元素的排列方式
  • flex-start:默认值,子元素从头开始依次排列
  • flex-end:从尾部开始排列
  • center:在主轴居中对齐
  • space-around:平分剩余空间
  • space-between:先两边贴边,在平分剩余空间(常用)
flex-wrap:设置子元素换行
  • flex布局中默认是不换行的,如果子元素超出父盒子宽度,则其子元素会自动缩小
  • wrap:子元素超出父盒子则会换行
align-items:设置侧轴上的子元素的排列方式(单行)
  • stretch:默认值,拉伸
  • center:垂直居中
  • flex-start:从上到下
align-content:设置侧轴上子元素的排列方式(多行)

网页开发经验

Iconfont的使用:

  • Font class使用时想要对iconfont引入样式,要注意权重
  • 对iconfont设置大小、颜色时,利用.iconfont.icon-对齐引入
  • 如果上下不对齐可以使用vericle-align属性使其对齐

SEO优化

  • title网站标题:是我们内页的第一个重要标签,是搜索引擎了解网页的入口和对网页主题归属的最佳判断点,建议:网站名-网页的介绍
  • description网站说明:简要说明网站主要是做什么的
  • keywords关键字:页面关键词,是搜索引擎的关注点之一

class命名

对于列表页,可以使用ul然后对其使用display布局

服务器

服务器:服务器是提供计算服务的设备,它也是一台计算机。在网络环境下,根据服务器提供的服务类型不同,服务器又分为文件服务器、数据库服务器、应用程序服务器、Web服务器等
远程服务器:通常是别的公司为我们提供的一台电脑,我们只需要把网站项目上传到这台电脑上,任何人就可以利用域名访问我们的网站了
免费的远程服务器:http://free.3v.do
  • 去免费空间网站注册账号
  • 记录下主机名、用户名、密码、域名
  • 利用cutftp软件上传网站到远程服务器
  • 在浏览器中输入域名,即可访问网站

JavaScript

介绍

ECMAScript是一种语言标准,JavaScript是对ECMAScript的一种实现
组成:核心(ECMAScript)、文档对象模型(DOM)、浏览器对象模型(BOM )
<script>
    /*内部js代码*/
    alert("HELLO,WORLD!");
</script>

<!--外部js代码-->
<script type="text/javascript" src="js/index.js"></script>

1.JS数据类型

1.1基本数据类型

  • number数值类型:1,-2,3,4.5
  • string字符串类型:‘123’,‘sunx’
  • boolean类型:0(假false)和1(真true),例:表达式 3 < 4 为true
  • undefined类型:当一个变量没有被定义、只被声明的时候
  • null空对象类型:typeof null = object

1.2类型之间的转换

  • 数值类型转换为字符串类型的隐式转换:num + “”
  • 利用toString()函数将数值类型转换为字符串类型
  • 利用Number()将字符串类型转换为数值类型

2.BOM

2.1BOM对象简介

BOM(浏览器对象模型)
  • window:alert()、confirm()、prompt()、setInterval()、setTimeout()
  • location:href、hash、url、reload()
  • srceen
  • history:go()

3.DOM

3.1 DOM结点类型

  • 元素结点(element node)
  • 文本结点(text node)
  • 属性结点(attribute node)

3.2 获取结点的方式

  • document.getElementById() :返回值为单个结点对象
  • document.getElementByTagName():返回值为结点对象集合,可以用for遍历
  • document.getElementsByClassName():返回值为结点对象集合

3.3 动态操作节点

  • 创建节点:createElement()
  • 插入节点:appendChild()、insertBefore(newNode,node)
  • 删除节点:removeChild()
  • 替换节点:replaceChild(newNode,node)
  • 创建文本节点:createTextNode()

4.offset、client、scroll

4.1 Offset

  • offsetWidth / offsetHeight : 用来获取对象自身的宽度和高度 ,包括内容、边框和内边距,即:offsetWidth / Height= width + border + padding
  • offsetLeft / offsetTop : 获取距离第一个有定位的父级盒子左边和上边的距离,注意:父级盒子必须要有定位,如果没有,则最终以body为准!offsetLeft和offsetTop从从父标签的padding开始计算,不包括border。即:从子盒子边框到定位父盒子边框的距离
  • offsetParent : 返回当前对象的父级(带有定位)盒子,可能是父亲、也可能是爷爷。如果当前元素的父级元素没有进行CSS定位(position:absolute 或 relative),则其offsetParent为body; 如果当前元素的父级元素中有CSS定位(position:absolute或relative),offsetParent取最近的那个父级元素。
offsetxxx和style.xxx的区别:
  • style.left只能获取行内的,而offsetLeft则可以获取到所有的
  • offsetLeft 可以返回没有定位盒子距离左侧的位置;而style.left不可以,其只能返回有定位盒子的left
  • offsetLeft 返回的是数字,而 style.left 返回的是字符串,除了数字外还带有单位:px
  • offsetLeft是只读的,而style.left是可读写
  • 如果没有给当前元素指定过top样式,则style.top 返回的是空字符串

4.2 Scroll

  • 网页正文全文宽 :document.body.scrollWidth
  • 网页正文全文高:document.body.scrollHeight
  • 网页被卷去的高:document.body.scrollTop
  • 网页被卷去的左:document.body.scrollLeft
  • 指定网页滚动到某个位置:scrollTo(x,y)
浏览器兼容写法:
var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
 
var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;

4.3 client

  • 网页可见区域宽: document.body.clientWidth
  • 网页可见区域高: document.body.clientHeight
  • clientLeft和clientTop:返回的是元素边框的 borderWidth,如果不指定一个边框或者不定位改元素,其值就为0

left和top分析:

  • clientLeft: 左边边框的宽度;clientTop: 上边边框的宽度
  • offsetLeft: 当前元素距离有定位的父盒子左边的距离;offsetTop: 当前元素距离有定位的父盒子上边的距离
  • scrollLeft: 左边滚动的长度; scrollTop: 上边滚动的长度

width和height分析

  • clientWidth/Height: 内容 + 内边距
  • offsetWidth/Height: 内容 + 内边距 + 边框
  • scrollWidth/Height: 滚动内容的宽度和高度

5 JS事件

5.1 JS事件流

定义:事件流描述的是从页面中接受事件的顺序,在JS中分为两种,一个是事件冒泡,还有一个是事件捕获。

5.2 事件处理

  • DOM 0级事件处理(如果想要删除事件,通过将事件程序属性赋值为null来实现)
    let btn = document.getElementById('btn');
        btn.onclick = function(){
            console.log('click');
        }
    
  • DOM 2级事件处理(事件的移除:btn.removeEventListener)
    const show = function () {
            console.log(this.id);
        }
    var btn = document.querySelector('input');
    btn.addEventListener('click', show);
    
总结:
/**
	DOM0级事件处理程序:btn.onclick
	DOM2级事件处理程序:btn.addEventListener(事件名称,处理函数,true/false)
	addEventListener('click',fn,false)  默认为FALSE,可以进行事件冒泡,改为TRUE事件则不会冒泡
	DOM2级事件程序,IE8以下不支持,IE8以下使用addEvent方法(此方法中的this指向window,可调用call()方法改变this的指向)

/**
 	相同点:如果同时出现HTML事件处理程序和 DOM0 级事件处理程序,DOM0级事件会覆盖HTML事件处理
 	不同点:除IE8以下的浏览器:先执行 DOM0 级事件,再执行 DOM2 级事件
 */
兼容性写法:
function addEvent(target, eventType, handler) {
    if (target.addEventListener) {
        target.addEventListener(eventType, handler, false);
    } else {
        target.attachEvent('on' + eventType, handler);
        handler.call(target)
    }
}

6 JS动画

6.1 JS动画简介

  • setTimeout(callback,time):延迟一段时间后执行callback方法,只执行一次
  • setInterval(callback,time):延迟一段时间后执行callback方法,循环执行,直到取消
  • clearInterval(计时器名称):清除定时器

6.2 一些简单的JS动画

  • 匀速运动
let timer = null
btn.onclick = function(){
    clearInterval(timer)
    timer = setInterval(function(){
        let left = div.offsetLeft
        //	当左边距<600px时,匀速运动
        if(left !== '600'){
            left += 50
            div.style.left = left + 'px'
        }else{
            clearInterval(timer)
        }
    },100)
}
  • 缓动运动(加速度 = (结束值 - 起始值) / 缓动系数 加速度由慢到快)
function startAnimation(obj,target){
    clearInterval(timer)
    timer = setInterval(function(){
        speed = target > obj.offsetLeft ? Math.ceil((target - box.offsetLeft) / 20 )
        		: Math.floor((target - box.offsetLeft) / 20)
        if(box.offsetLeft === target){
            clearInterval(timer)
        }
        box.style.left = box.offsetLeft + speed + 'px'
    },30)
}
  • 多值运动
startAnimation(box, 'height', 80, function () {
	startAnimation(box, 'width', 200, function () {
		startAnimation(box, 'opacity', 30)
	})
})

function startAnimation(obj,attr,target,fn){
     //	针对多值运动,定时器的返回值要绑定当前的对象中
    clearInterval(obj.timer)
    obj.timer = setInterval(function(){
        let cur = 0,speed = 0;
        //	透明度变化处理
		if(attr === 'opacity'){
            cur = Math.round(parseFloat(getStyle(obj,attr)) * 100)
        }else{
            cur = parseInt(getStyle(obj,attr))
        }
        
        //	1.求速度
        speed = target > cur ? Math.ceil((target - cur) / 20)
        			: Math.floor((target - cur) / 20)
        // 	2.临界处理
        if(targer === cur){
            clearInterval(obj.timer)
            if(fn){
                fn()
            }
            return 
        }
        
        //	3.运动
        if(attr = 'opacity'){
            obj.style[attr] = `alpha(opacity : ${cur + speed})`
            obj.style[attr] = (speed + cur) / 100
        }else{
            obj.style[attr] = cur + speed + 'px'
        }
    },30)
}

function getStyle(obj,attr){
    if(obj.currentStyle){
        //	兼容IE浏览器
        return obj.currentStyle[attr]
    }else{
        //	兼容主流浏览器
        return getComputedStyle(obj,null)[attr]
    }
}
  • myAnimation.js
function startAnimation(obj, json, fn) {
    //针对多值运动,定时器的返回值要绑定当前的对象中
    clearInterval(obj.timer)
    obj.timer = setInterval(function () {
        let flag = true;
        let cur = 0, speed = 0;
        //透明度变化处理
        for (let attr in json) {
            switch (attr) {
                case 'opacity':
                    cur = Math.round(parseFloat(getStyle(obj, attr)) * 100)
                    break;
                case 'scrollTop':
                    cur = obj[attr]
                    break;
                default:
                    cur = parseInt(getStyle(obj, attr))
                    break;
            }

            // console.log(attr, obj[attr])
            // console.log(cur,flag)

            //1.求速度
            speed = json[attr] > cur ? Math.ceil((json[attr] - cur) / 20)
                : Math.floor((json[attr] - cur) / 20)

            //2.临界处理
            if (json[attr] != cur) {
                flag = false
            }

            //3.运动起来

            switch (attr) {
                case 'opacity':
                    obj.style[attr] = `alpha(opacity : ${cur + speed})`
                    obj.style[attr] = (speed + cur) / 100
                    break;
                case 'scrollTop':
                    obj.scrollTop = cur + speed
                    break;
                default:
                    obj.style[attr] = cur + speed + 'px'
                    break;
            }

            if (flag) {
                clearInterval(obj.timer)
                if (fn) {
                    fn()
                }
                return
            }

        }

    }, 10)
}

function getStyle(obj, attr) {
    if (obj.currentStyle) {
        //兼容IE浏览器
        return obj.currentStyle[attr]
    } else {
        //兼容主流浏览器
        return getComputedStyle(obj, null)[attr]
    }
}

7 函数

7.1 函数的声明

  • funtion fn()
  • let fun = function fn():fn为形参,只能在函数体内使用
  • Function构造函数:let sub = new Function(‘x’, ‘y’, ‘return x - y’)

7.2 函数的返回值

  • 若函数无返回值,打印输入undefined
  • 如果返回值为对象,则返回该对象

7.3 函数的调用方式

  • 普通函数调用:fun(),this指向window对象
  • 构造函数方法的方式调用:this指向当前构造函数的对象
    let obj = {
            a: 100,
            fn: function () {
                //  this表示obj对象
                console.log(this)   //  this指向当前的对象
                console.log('fn函数被调用')
            },
            fn2: function () {
                console.log(this)
                this.a = 2
            }
    
        }
    obj.fn()    // 打印obj对象
    obj.fn2()   // 打印obj对象
    console.log(obj.a)  // a = 2
    
  • 构造函数调用:this该构造函数
    //  3.间接调用模式:使用 call()、apply() 方法,call方法和apply方法会改变第一个参数的this指向
    let arrObj = {
        a: 10
    }
    
    function sum(a, b) {
        console.log(this)
        return a + b;
    }
    
    console.log("call方法调用的结果:" + sum.call(sum, 1, 3))   //  打印该函数体
    console.log("apply方法调用的结果:" + sum.apply(arrObj, [1, 3]))    // 打印arrObj对象
    

7.4 函数的参数

  • 可以传递任何类型的参数(非严格模式’use strict’可以传递同名参数)
  • 可以使用同名参数,返回结果为最后一个参数
  • 若实际参数比形式参数少,剩下的形参为undefined类型
  • 若形参个数比实参多,考虑使用arguments
    //  arguments不是真正的数组,他是类数组,且能通过[]访问它的每一个元素
    function argueFun() {
        console.log("形参个数" + arguments.length)
        let sum = 0
        for (let i = 0; i < arguments.length; i++) {
            sum += arguments[i]
        }
        console.log(sum)
    }
    

7.5 函数的重载

重载:定义相同的函数名,传入不同的参数(js为弱类型语言,数据类型分的不是很清楚,所以不存在重载),在js中,当函数名相同时,默认最后一个声明的函数有效
//  引入argument实现函数的重载功能
function doAdd() {
    if (arguments.length === 0) {
        return 10
    } else if (arguments.length === 1) {
        return arguments[0] + 10
    } else if (arguments.length === 2) {
        return arguments[0] + arguments[1] + 10
    }
}

console.log("无参doAdd" + doAdd())
console.log("一个参数doAdd" + doAdd(10))
console.log("两个参数doAdd" + doAdd(10, 20))

7.6 函数的参数传递

  • 基本数据类型的传递:在向参数传递基本数据类型的值时,被传递的值会被赋值给另一个局部变量
  • 引用数据类型的传递:在向参数传递引用数据类型的值时,会把这个值在内存中的地址赋值给局部变量
    function setName(obj) {
        obj.name = 'sun1566'
    }
    
    let Person = {}
    setName(Person)
    console.log("Person的姓名:" + Person.name)
    
    //  函数的重写
    function setName2(obj) {
        obj.name = 'sun1566'
        console.log("Person2的姓名:" + Person2.name)
        obj = {}            //  obj对象开辟了一个新的内存空间
        obj.name = 'sunX'   //  将obj对象的name属性赋值’sunX‘,并没有改变Person2地址的内容
        console.log("Person2的姓名:" + Person2.name)   //打印的结果还是为sun1566
    }
    
    let Person2 = {}
    setName2(Person2)
    

7.7 函数的属性

  • length属性:arguments对象中的length属性表示实参的长度,而函数的length属性表示形参的长度
  • prototype属性:指向函数的引用,可以为prototype赋属性,如 fn.prototype.a = 1
  • name属性:函数的名称

7.8 call方法和apply方法

作用:这两个函数都是在特定的作用域中调用函数,能改变函数的作用域,实际上是改变函数体内 的this指向
区别:call()可以接收任何类型的参数,而apply()只能接收数组参数。
//  1.找出数组的最大元素 Math.max,apply(null,[])
console.log(Math.max(1, 2, 3, 4, 5))
let arr = [34, 12, 42, 22]
let max = Math.max.apply(null, arr)
console.log(max)

//  2.将类数组转换为真正的数组
function add() {
    console.log(arguments)      //  打印实参数组对象(Object)
    //  通过slice()方法可以实现浅拷贝(只拷贝内容,且对拷贝的内容进行修改不会改变原内容),slice的返回值为数组
    let arr = Array.prototype.slice.apply(arguments)
    console.log(arr)    //  此时的arr会变成真正的数组
    let arr2 = [].slice.call(arguments)
    console.log(arr2)
    let arr3 = Array.prototype.slice.apply({0: 'hello', 1: 'world', length: 2})
    console.log(arr3)
}

add(1, 2, 3, 4, 5)

//  3.数组连接
let arr2 = [5, 5]
let num = Array.prototype.push.apply(arr2, [1, 2, 3, 0])    //push的返回值为数组的长度
console.log("arr2:" + arr2)
console.log("num:" + num)

//  4.利用call和apply做继承
function Animal(name, age) {
    this.name = name
    this.age = age
    this.sayAge = function () {
        console.log(this.name + "今年" + this.age + "岁了")
    }
}

function Cat(name, age) {
    //  继承了animal
    //  call的变量之间用','隔开
    Animal.call(this, name, age)      //  把this指向Cat实例对象
}

let c = new Cat('小猫咪', 20)
c.sayAge()
console.log(c)

function Dog() {
    //  继承了animal
    //  apply的第二个参数为数组对象
    Animal.apply(this, arguments)      //  把this指向Cat实例对象
}

let d = new Dog('小狗狗', 3)
d.sayAge()
console.log(d)

7.9 函数柯里化

定义:柯里化通常也称部分求值,其含义是给函数分布传递参数,每次传递参数进行处理,并返回一个更具体的函数接收剩下的参数,这中间可嵌套多层这样的接收部分参数函数,直至返回最后结果
//  柯里化写法
function add(x) {
    return function (y) {
        return x + y
    }
}
console.log(add(4)(5))

8 JS作用域

8.1 JS作用域内部原理

  • 编译:分词、分词、代码生成,编译器把程序分解成词法单元,将自发单元解析成AST,再把AST转换成机器指令,等待执行的过程(边解释边执行)
  • 执行:引擎运行代码时,首先查找当前的作用域,看变量是否在当前的作用域,如果在,引擎就会直接使用这个变量,否则,引擎会继续向上查找
  • 查找:在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中查找,知道找到该变量,或者抵达最外层作用域(全局作用域)为止
    function fn() {
        //  在严格模式下 'use strict',没有定义变量的关键字会报错
        abs = 1;    //  没有声明abs的关键字,默认会在window中创建abs变量
    }
    
  • 嵌套:在多层的嵌套作用域可以定义同名的标识符,这叫做遮蔽效应
  • 异常:引擎在最外层(全局作用域)中查找不到变量时,就会触发异常

8.2 变量声明的提升

aaa = 2
var aaa    //  var会存在变量提升,var声明变量会将变量提升到最上面执行,这个过程叫预解释
console.log(this)   //  aaa会存入window对象

//声明函数的提升:直接声明函数会提升,但声明函数表达式不会提升(报错)
fun()	(√)	//存在变量提升
function fun() {
    console.log("fun函数被调用")	
}

foo()	(×)//函数表达式声明,不会提升
let foo = function f() {
    console.log("foo函数被调用")	
}
函数声明和变量声明
  • 若只对变量声明但未对其赋值,函数的声明会优先于未赋值的变量声明,且函数的声明会覆盖同名未赋值的变量声明
  • 若定义了变量且对其赋值了,且函数名和变量名相同,则最后声明的会覆盖前面的声明

8.3 作用域

  • 全局作用域:代码在程序的任何地方都能被访问,window对象的内置属性都拥有全局作用域
  • 函数作用域:在固定的代码片段才能被访问

8.4 作用域链

作用域链:一般情况下,变量取值到创建这个变量的函数的作用域中取值,但如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这样的查找过程形成的链条就叫做作用域链。
var a = 1
var b = 2

function f1(x) {
    console.log(a)  //  var a , 因此打印undefined
    var a = 10  // var定义的变量会进行变量提升,提升到函数体的最上方
    function bar(x) {
        var a = 100
        b = x + a
        return b
    }

    bar(20)
    //  bar(20)的执行环境:x:20 、 a:undefined 、arguments:0 、 this:window
    bar(200)
}

console.log("f1:" + f1(0))
//  f1(0)的执行环境(存储的变量对象):x:0 、 a:undefined 、 bar:function() 、 arguments:0 、 this:window

9 闭包

9.1 闭包的概念

  • 闭包:定义在函数内部的函数,能使得全局作用域读取函数作用域的变量
  • 特点:闭包可以记住诞生的环境,比如fn2可以记住它诞生的环境fn1,所以fn2中可以得到fn1中的内部变量
  • 本质:闭包就是函数内部和函数外部链接的一座桥梁
        let a = 123
        function fn1(a){
            let b = 234
            function fn2(b){
                return b
            }
            return fn2
        }
    
        let fn = fn1()
        console.log(b)	//b为全局作用域中的变量,直接打印fn2中b的值,会报错
        console.log(fn())
    

9.2 闭包的用途

  • 计数器:读取了函数内部的变量,这些变量始终在内存中,在使用闭包时,要小心内存的泄露
        function count(){
            let start = 0;
            function add(){
                return ++start
            }
            return add
        }
    
        let fn = count()
        setInterval(function(){
            console.log(fn())
        },1000)
        fun = null	//销毁变量,释放内存
    
  • 能够封装对象的私有属性和方法
        function Person(name) {
            //  私有属性
            let age
    
            //  私有的方法
            function setAge(n) {
                age = n
            }
    
            function getAge() {
                return age
            }
    
            return {
                name: name,
                setAge: setAge,
                getAge: getAge
            }
        }
    
        let p = new Person()
        p.setAge(22)
        console.log(p.getAge())
    
注意点:
  • 使用闭包使得函数中的变量始终在内存中,内存消耗很大,所以不能滥用闭包,否则会造成页面的性能问题
  • 每个父函数调用完成,都会形成新的闭包,父函数中的变量始终会在内存中,相当于缓存
    function count() {
        let start = 0

        return function add() {
            return ++start
        }
        //  return add
    }

    //  count()()相当于每次都生成一个新的闭包,因此每次都会被初始化
    console.log(count()())
    console.log(count()())
    //  正确调用方式:
    let inc = count()
    console.log(inc())
    console.log(inc())

    //  使用完销毁变量
    inc = null

9.3 立即执行函数

定义:定义函数之后,立即调用该函数,这种函数叫立即执行函数(注:()跟在函数后面,表示函数被调用)
两种写法:
  • (function(){})();
  • (function(){}());
    //  立即执行函数也叫闭包,可以封装私有的属性,同时可以减少对全局变量的污染
    let fun = (function () {
        //  私有属性
        let count = 0
        return function () {
            return ++count
        }
    })();

    console.log("第一次执行fun函数" + fun())
    console.log("第二次执行fun函数" + fun())
    console.log("第三次执行fun函数" + fun())

9.4 对循环和闭包的错误理解

function f() {
        var arr = []
        for (var i = 0; i < 10; i++) {
            arr[i] = function () {      //根据作用域链,当循环体执行完才进行arr[i]函数操作,arr[i]执行的是i++也就是此时的i=10
                return i
            }
        }
        return arr
    }

    var fn1 = f()
    console.log("f函数体" + fn1)
    console.log(fn1[0]())
    console.log(fn1[1]())

    //  解决方法一:使用let块级作用域
    function foo() {
        let arr = []
        for (let i = 0; i < 10; i++) {
            arr[i] = function () {
                return i
            }
        }
        return arr
    }

    let bar = foo()
    console.log(bar[0]())

    //	解决方法二:使用闭包
    function fn() {
        var arr = []
        for (var i = 0; i < 10; i++) {
            arr[i] = (function (n) {
                return function () {
                    return n
                }
            })(i);
        }
        return arr
    }

    var fn2 = fn()
    console.log(fn2[0]())

    //  解决方法三:使用立即执行函数
    function fun() {
        var arr = []
        for (var i = 0; i < 10; i++) {
            (function (n) {
                arr[n] = function () {
                    return n
                }
            })(i)
        }
        return arr
    }

    console.log("fun函数体" + fun())
    var fn3 = fun()
    console.log(fn3[0]())
    console.log(fn3[1]())
    console.log(fn3[2]())

9.5 闭包的使用场景

  • 返回值
        function fn1() {
            let i = 1
            return function () {
                return i
            }
        }
    
        let fn11 = fn1()
        console.log("执行aa函数的结果:" + fn11())
    
  • 函数赋值:将内部函数赋值给外部的变量
        let fn22;
        let fn2 = function () {
            let name = 'sunX'
            let fn = function () {
                return name
            }
            fn22 = fn()
        }
        fn2()
        console.log("执行fn2函数的结果:" + fn22)
    
  • 获取函数参数
        function fn33() {
            let name = 'sunX'
            let a = function () {
                return name
            }
            fn3(a)
        }
    
        let fn3 = function (a) {
            console.log("执行fn3函数的结果:" + a())
        }
    
        fn33()
    
  • IIFE:立即执行函数
        let fn4 = function (a) {
            console.log("执行fn4函数的结果:" + a())
        }
    
        !(function () {
            let name = 'sunXian'
            let a = function () {
                return name
            }
            fn4(a)
        })();
    
  • 循环赋值
        let fn = function(){
            let arr = []
            for(let i = 0 ; i < 10 ; i++){
                arr[i] = (function(n){
                    return function(){
                        return n
                    }
                })(i)
            }
            return arr
        }
    
        console.log(fn[5]())	//	打印输出5
    
  • get和set方法:将要操作的变量保存在函数内部,防止暴露在外部
  • 计数器和迭代器
        function setUp(arr) {
            let i = 0
            return function () {
                return arr[i++]
            }
        }
    
        let next = setUp(['aaa', 'bbb', 'ccc', 'ddd'])
        console.log(next())		//	打印输出第一个参数 'aaa'
        console.log(next())		//	打印输出第二个参数 'bbb'
    
  • 区分首次加载
        function firstLoad(num) {
            let arr = []
            return function () {
                if (arr.indexOf(num) >= 0) {
                    return false
                } else {
                    arr.push(num)
                    return true
                }
            }
        }
    
        let flag = firstLoad(10)
        console.log(flag())
        console.log(flag())
    
  • 模拟缓存机制:模拟一个对象的key,看该对象中是否有相同的key,如果有,直接获取value返回
        let mult = (function(){
            let cache = {}
            let calculate = function(){
                let sum = 0
                for(let i = 0 ; i < arguments.length ; i++){
                    sum += arguments[i]
                }
                return sum
            }
            //	对cache对象进行操作
            return function(){
                let args = Array.prototype.join.call(arguments,',')
                if(args in cache){
                    return cache[args]
                }
                cache[args] = calculate.apply(null,arguments)
                return cache[args]
            }
        })()
    
        console.log(multi(1, 2, 3, 4, 11, 8))
        console.log(multi(1, 2, 3, 4, 11, 8))
        console.log(multi(1, 2, 3, 4, 11, 8, 14))
    

第十章 深入理解this指向

10.1 this的默认绑定

  • 全局环境下的this指向了window
  • 函数独立调用,函数内部的this也指向window
  • 被嵌套的函数独立调用时,this也指向window
  • IIFE立即执行函数:内部this指向window
  • 当做构造函数调用:this指向当前的构造函数
  • 当做构造函数的方法调用:this指向当前对象

10.2 隐式丢失

隐式丢失:被隐式绑定的函数丢失了绑定对象,从而默认绑定到window,这种情况比较容易出错却又非常常见
  • 函数别名
        var a = 0
    
        let bar = function () {
            console.log(this.a)
        }
    
        let obj = {
            a: 1,
            foo: bar(),
            fo: bar
        }
    
        //  把obj.foo赋值给别名bar,造成隐式丢失的情况,因为只是把obj.foo()赋值给了bar变量,而bar与		obj对象毫无关系
        obj.foo       //  相当于 bar(),因此打印输出的是window下的a
        obj.fo()
    
  • 参数传递
        var b = 0
        function fn(){
            console.log(this.b)
        }
        function bar(fun){
            fun()
        }
        let obj = {
            b : 0,
            fn:fn
        }
        //	把object的fn当做参数传递到bar函数中,有隐式的函数赋值,fn=obj.fn,只是把fn函数赋值给了			fun,而fun和obj毫无关系
        bar(obj.fn)
        bar(function(){
            console.log(this.b)
        })
    
  • 内置函数:setTimeout和setInterval第一个参数的回调函数中的this默认指向了window
  • 间接调用
        var c = 100
        function f() {
            console.log(this.c)
        }
        let hhh = {
            c: 66,
            f: f
        }
        //  隐式绑定:函数当做对象的方法使用,内部的this指向了该对象
        hhh.f()
    
        let p = {c: 33}
    
        //  将hhh.f赋值给p.f函数,之后p.f函数再执行,其实是属于p对象的方法的指向,this指向了当前p对象
        p.f = hhh.f
        p.f()      //   打印输出p的属性c
    
        //  立即调用:将f函数对象赋值给p的f函数,然后立即执行,相当于f函数的立即调用,内部的this默认指向			了window
        !(p.f = hhh.f)();   //  指向window下的c
    
  • 其他情况:指向了window的特殊情况
        var a = 0
    
        let bar = function () {
            console.log(this.a)
        }
    
        let obj = {
            a: 1,
            fo: bar
        }
    
        !(obj.fo = object.fo)();
        !(false || object.fo)();
        !(1, object.fo)();
    

10.3 显示绑定

  • 将对象绑定到this上的三种方式:call()、apply()、bind()
       var a = '我是window下的a'
    
        function fn() {
            console.log(this.a)
        }
    
        let obj = {
            a: '我是obj对象下的a'
        }
    
        fn()
        fn.call(obj)
        fn.apply(obj)
        let f = fn.bind(obj)    //  将fn函数绑定到obj中,并返回函数f
        f()
    
  • 硬绑定:显示绑定的变形
        let b = '我是window下的b'
        function fun() {
            console.log(this.b)
        }
        let ob = {
            b: '我是ob对象下的b'
        }
        let bar = function () {
            fun.call(ob)
        }
    
        //	都输出'我是ob对象下的b'
        bar()
        setTimeout(bar, 1000)
        bar.call(window)
    
  • 数组的forEach(fn,对象)、map()、filter()、some()、every()的第二个参数都可以改变this指向
        var c = 'window下的c'
        function foo(el) {
            console.log(el, this.c)
        }
        let o = {
            c: 'o对象下的c'
        }
        let arr = [11, 22, 33]
        //	只传递一个参数,因此打印输出'window下的c'
        arr.forEach(foo)
        
    	//	第二个参数为o对象,打印输出'o对象下的c'
        arr.forEach(function (el, index) {
            console.log(el, index, this)
        }, o)
    

10.4 new绑定

  • 使用new关键字实例化一个对象,此时的this指向当前对象
        function f() {
            console.log(this)
        }
    
        f()		//	打印输出window
        new f()	//	打印输出'f()函数'
    
  • 使用return关键字来返回对象的时候,实例化出来的对象是当前的返回对象
        function fn() {
            console.log(this) 	//	因为实例化了对象,因此打印输出fn()
            return {
                name: 'sunX'
            }
        }
    
        let Fn = new fn()
        console.log(Fn)		//	打印输出return返回的对象{name:'sunX'}
    
        let Person = {
            fav: function () {
                return this
            }
        }
    
        let p = new Person.fav()	
        console.log(p)	//	由于是实例化的对象,因此打印输出fav{}
    
        //  实例化出来的对象内部的属性constructor属性指向了当前的构造函数
        console.log(p.constructor === Person.fav)
    

10.5 严格模式下的this指向

  • 在严格模式下,独立调用的函数内部的this指向undefined
        function f() {
            'use strict'
            console.log(this)
        }
    
        f()		//	输出undefined
    
  • 函数apply()和call():始终是他们的第一个参数
    var color = 'red'
    function showColor() {
        'use strict'
        console.log(this)			//	this指向undefined
        console.log(this.color)		//	报错
    }
    showColor.call(undefined)
  • 若在非严格模式,传入null、undefined,this都会指向window,但非严格模式下,call()后面传入什 么,this就指向什么

你可能感兴趣的:(前端,前端)