前端面试题(1)

1,CSS盒子模型
CSS的盒模型有两种:标准盒模型,IE盒模型。

IE盒模型,其中content包含了内边距padding和边框border。盒子的实际宽度 = content+margin。

标准盒模型,盒子的实际宽度 = content(内容)+padding(内边距)+border(边框)+margin(外边距)

CSS3新增一种盒模型计算方式,box-sizing属性,这个属性有3个值,content-box (默认值),padding-box  (存在兼容性),border-box当取此值,该元素的content就包含了padding和border,换言之,可以把标准盒模型转化为 IE盒模型。
2,样式初始化作用
因为浏览器兼容问题,不同浏览器对有些标签的默认值是不一样的,如果没有对css样式进行初始化设置,往往会出现浏览器之间页面显示差异。样式初始化会对SEO有一定影响,但力求影响最小的情况下进行初始化。
//正式项目中,一般不用
{ margin:0; padding:0 }
3,语义化理解
用正确的标签做正确的事情。

优点:便于开发者阅读,写出更优雅的代码;便于团队开发和维护,语义化更具有可读性;让浏览器爬虫与机器更好的解析。

语义化标签:header,nav,article,section,aside,footer
4,对web标准的理解
web标准不是某一个标准,而是一系列标准的集合。页面主要有三部分组成:结构,表现和行为。

W3C对web提出了一些规范化要求:1) 标签字母要小写。2)标签要闭合。3)标签不允许随意嵌套。4)尽量使用外链css样式和js脚本,link和script。5)标签的id和class等属性命名要做到见文知义,标签越少,加载越快,用户体验提高,代码维护简单,便于改版。
5,一个页面从输入 url 到页面加载显示完成经历的过程。
(1)浏览器根据请求的url交给DSN域名解析,找到真实IP,然后向服务器发送请求。

(2)服务器交给后台处理,完成后返回数据,浏览器接受文件(HTML,JS,CSS,图像等)。

(3)浏览器对加载到的资源(HTML,JS,CSS等)进行语法解析,建立相应的内部数据结构(DOM树等)。

(4)载入解析到的资源文件,渲染页面完成。
6,HTML常见的块级元素和行内元素
6.1,常见的块级元素
 	块级元素特点:(1)所有的块级元素都会独占一行;(2)块级元素可以直接设置高度和宽度;(3)如果一个块级元素没有设置宽度,那么其默认的宽度是父元素的宽度。
 	```

```shell
div, p, h1-h6, ul, ol, li, table, th, td, dl, dt, dd, address, audio, canvas
6.2,常见的内联元素
行内元素特点:(1)所有的行内元素都在一行上显示;(2)行内元素设置的宽度和高度不生效。
a, b, span, big, del, em, i, strong, sub, sup, video
6.3,常见的行内块元素
行内块元素特点:(1)与所有的行内元素相同都在一行上显示;(2)与块元素一样可以设置宽度与高度。
img, input, select, button, textarea
6.4, 常见的空元素
br, hr, img, input, link, meta
6.5,不同元素之间的转换方式
转换为块级元素:  display:block 

转换为行内块元素: display:inline-block 

转换为行内元素: display: inline
7, HTML语义化标签
根据内容的结构化,选择合适的标签,便于开发者阅读,写出更优雅的代码,让浏览器爬虫和浏览器很好的解析。
7.1,常见的语义化标签
header, nav, article, section, aside, footer, h1-h6, title, main, strong, em, address
好处:

(1)为了在没有css样式情况下,也可以很好地呈现出内容结构,代码结构。

(2)提高用户体验:title,alt用于解释名词或解释图片信息。

(3)有利于SEO: 和搜索引擎建立良好沟通,有助于爬虫抓取更多的有效信息,爬虫依赖于标签来确定上下文和各个关键字的权重;

(4)方便其他设备解析。(盲人阅读器,移动设备。)

(5)便于团队开发和维护,语义化更具有可读性。
8,css position定位
position定位有4个值: static,relative,absolute,fixed 

(1)static:默认值,没有定位,元素出现在正常文档流中。静态定位的元素不会受到top,bootom,left,right  影响。

(2 relative:相对定位,相对于正常文档流位置产生移动,元素仍出现在正常文档流中。会受top,bottom, left,right影响。

(3)absolute:绝对定位,脱离文档流,绝对定位的元素的位置相对于最近的已定位的父元素,如果该元素的父级元素没有相对定位,那么它的位置相对于html 。

(4)fixed:固定定位,脱离文档流,元素的位置相对于浏览器窗口固定位置。即使浏览器窗口是滚动的它也不会移动。
9,元素水平垂直居中
//html
<div class="parent" style="width:400px;height:200px;background:red">
	<div class="child" style="width:200px;height:100px;background:green"></div>
</div>
// 方法一:子元素相对定位 
// 这种方法只在子元素宽高已知的情况下适用。
.parent{
            
}
.child{
  position: relative;
  left:100px;
  top:50px
}
// 方法二:子元素相对于父元素绝对定位,并配合使用负值的 margin
// 最常用的方法,这种方法只能在子元素宽高已知的情况下使用。
.parent{
   position: relative;
}
.child{
   position: absolute;
   top:50%;
   left:50%;
   margin-top: -50px;
   margin-left: -100px
}
// 方法三:子元素相对父元素绝对定位,并使用calc()计算属性
// 思想上同上,只不过用计算属性代替了偏移量和margin,这种方式同样只在子元素宽高已知的情况下使用。
.parent{
   position: relative;
}
.child{
   position: absolute;
   top:calc(50% - 50px);
   left:calc(50% - 100px)
}
// 方法四:子元素相对于父元素绝对定位,并使用 transform属性

这种方式和第二、第三方法类似,只不过是用了transform偏移量实现了负值 margin的效果。不同的地方在于,使用  transform  对子元素的宽高没有要求,在未知宽高的情况下依然适用。这种方式在子元素是单行或多行文本的时候要求垂直居中的情况下十分适用。
.parent{
   position: relative;
}
.child{
   position: absolute;
   top:50%;
   left:50%;
   transform:translate(-50%,-50%)
}
// 方法五:子元素相对于父元素绝对定位(偏移量都是0),子元素设置margin:auto

// 这种方式同样只在子元素宽高已知的情况下使用。
.parent{
   position: relative;
}
.child{
   position: absolute;
   top:0;
   right:0;
   bottom:0;
   left:0;
   margin:auto
}
// 方法六:父元素display:flex,子元素margin:auto

// 最简单的方式,子元素宽度已知或者未知的情况都适用
.parent{
   display:flex
}
.child{
   margin:auto;
}
// 方法七:父元素设置 display:flex,以及内容的水平和垂直居中
.parent{
   display:flex;
   justify-content: center;
   align-items: center
}
.child{
            
}
// 方法八:通过line-height和align-text实现块内元素中,行内元素居中

// 适用条件:包裹的必须是行内元素
.parent{
   text-align: center;
   line-height: 200px
}
.child{
            
}

<div class='parent' style='width:400px;height: 200px;background: red;'>
  <span>这里必须是块内元素</span>
</div>
10,BFC 块级格式化上下文
BFC: (Block Formatting context) 块级格式化上下文 ,BFC 就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,并且在一个BFC中,块盒与行盒都会垂直的沿着其父元素的边框排列。

// 10.1,如何创建BFC

(1)float 的值不是none。例如:left,right。

(2)position的值不是static或者relative。例如:absolite,fixed 。

(3 display的值是inline-block,inline-flex, flex,table-cell,table-caption。

(4)overflow的值不是visible。例如:hiddle 。
11,闭包
闭包其实就是一个函数,只不过该函数能访问其他函数里的内容变量,这些变量不会因函数执行完毕而销毁,而是始终保存在内存中。

作用:闭包能够保护函数内的变量安全;不会造成全局变量的污染。

缺点:占用内存比较大,还需要手动释放。
12,原型与原型链
原型:javascript中每个函数都存在有一个原型对象属性prototype,并且所有函数的默认原型都是Object的实例。

原型链:当访问一个对象的某个属性时,先在自身属性中查找,找到返回;如果没有找到,则沿着它的__proto__ 隐式原型上查找,找到返回;如果最终还没有找到,则返回undefined。
13 用一个 div 实现旋转的图片
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>动画</title>
    <style>
        *{margin:0;padding:0}
        @keyframes mycircle{
            to{
                transform:rotate(0deg)
            }
            from{
                transfrom:rotate(360deg)
            }
        }
        img{
            width:200px;
            height:200px;
            border-radius:50%;
            animation:mycircle 3s linear infinite
        }
    </style>
</head>
<body>
    <img src="./circle.jpg" alt="">
</body>
</html>
13.1 动画( animation ),转换( transform )和 过渡 (transition):
// animation 至少包含两个属性:1)动画名称;2)完成一个周期所需要的时间。
// 使用方法:
	animation:动画名称 完成一次动画所需的时间 动画运行速度 动画播放的次数

// 动画名称就是说 @keyframes 动画名称 {}
// transform 属性应用于元素的 2D 或者 3D 转换,这个属性允许你将元素旋转,缩放,移动,倾斜等。

// 旋转:transform:rotate(旋转度数)
transform:rotate(30deg) //元素旋转30度

// 缩放:transform:scale(x,y) 参数代表: X 轴和 Y 轴缩放比例。
transform:scale(1.2) // X轴和Y轴同时放大1.2倍

// 移动:transform:translate(X,Y) 参数:移动的最终坐标。
// transform:translateX()或者transform:translateY()
transform:translate(200,300)

// 倾斜:transform:skew(x-angle,y-angle)
// transition 样式过渡,从一种效果逐渐改变为另一种效果
// 用法:transition: 属性名 完成过渡需要的时间 过渡的速度 何时开始

div{
   width:100px;
   height:100px;
   background: red;
   transition:width 4s linear 1s;
}
div:hover{
   width:300px;
}
14 Flex 布局
flex布局是一种弹性布局,布局样式比较灵活,在大多数情况下可以代替float而且不会脱离文档流。

要想让父容器变成flex布局,则必须设置display:flex  。
14.1父容器属性
// 1. 用于父元素的样式:

(1)flex-direction :设置主轴方向。

取值: row  (向右)  |    row-reverse  (向左)   |    column  (向下) |  column-reverse   (向上)

(2) flex-wrap  : 是否换行。

取值: nowrap  (不换行)   |    wrap  (换行)

(3) flex-flow  : 是  flex-direction  和  flex-wrap  的缩写。默认  flex-flow : row nowrap 

(4) justify-content  :决定子元素水平方向排列方式。(假设主轴方向向右,即  flex-direction:row )

取值: flex-start  (自左向右排列) |  flex-end  (自右向左排列) |  center  (居中) |  space-between  (两端对齐) |  space-around  (均匀分布)

(5) align-items  : 决定子元素垂直方向排列方式。(假设主轴方向向右,即  flex-direction:row )

取值: flex-start  (自上而下排列) |  flex-end  (自下而上排列) |  center  (居中) |  baseline  (第一个子元素的文字基线对齐) | stretch (子元素高度会和父元素高度一样高)

(6) align-content  : 父元素所包含的行在交叉方向有空余空间时,如何分别空间。属性定义了多根轴线的对齐方式,如果项目自有一根轴线,该属性不起作用。

取值: flex-start  (自上而下排列) |  flex-end  (自下而上排列) |  center  (居中) |  baseline  (第一个子元素的文字基线对齐) |  stretch  (子元素高度会和父元素高度一样高)
// 2. 用于子元素的样式

(1) order  : 定义子元素的排列顺序。数组越小,排列越靠前,默认为 0。

(2) flex-grow  : 定义子元素的放大比例,默认 0,即如果存在剩余空间,也不放大。

-如果所有的子元素的  flex-grow  属性值都是 1( flex-grow:1 ),则它们将等分剩余空间。
-如果一个子元素的  flex-grow:2 ,其他的子元素  flex-grow:1 ,则前者占据的剩余空间将比其他子元素多一倍。

(3) flex-shrink :定义子元素的缩小比例,默认值1,即如果空间不足,该值子元素将缩小。

如果所有子元素的  flex-shrink  属性都是 1( flex-shrink:1 ),则都将等比缩小。
如果一个子元素的  flex-shrink:0 ,其他项目都为  flex-shrink:1 ,则空间不足时,前者不缩小。

(4) flex-basis : 定义在分配多余空间之前,子元素占据的主轴空间。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为  auto ,即项目的本来大小。

它可以设置为跟  width  或者  heigh  属性一样的值(比如  350px ),则项目将占据固定空间。

(5) flex : 是  flex-grow , flex-shrink  和  flex-basis  的简写。默认值  flex:0 1 auto 。后两个属性可选。

(6) align-self : 允许单个子元素有与其他子元素不样的对齐方式,可覆盖  align-items  属性。

取值: auto  (默认值,表示继承父元素的  align-items  属性,如果没有父元素,则等同于  stretch ) |  flex-start  (自上而下排列) |  flex-end  (自下而上排列) |  center  (居中) |  baseline  (第一个子元素的文字基线对齐) |  stretch  (子元素高度会和父元素高度一样高)
15 ul 和 ol 列表
// ul 无序列表
// ol 有序列表
// 无序列表  <===>  有序列表 通过 list-style-type
//无序列表取值:list-style-type:disc(实心圆) | circle(空心圆) | aquare(实心方块) | none(无)
//有序列表取值:list-style-type:decimal(阿拉伯数字带圆点) | lower-roman(小写罗马数字) |
// upper-roman(小写罗马数字) | lower-alpha(小写英文字母) | upper-alpha | none

//list-style-image:url(图片地址)  图片作项目符号。
//list-style-position:outside | inside 图片相对于li列表项内容位置,outside表示在li标签内。
//list-style:列表的复合属性
//语法:list-style:url() none outside.
16 各个浏览器内核和前缀
Chrom(谷歌浏览器)    |  blink内核   |  -webkit-
Firefox(火狐浏览器)  |  Gecko内核   |  -moz-
IE(IE浏览器)        |  Trident内核  |  -ms-
Opera(欧朋浏览器)    |  presto内核   |  -o-
17 img 标签边距问题解决办法
原因:块级元素包含内联元素如图片文字等时,内联元素默认是和父级元素的  baseline (基线)对齐的,而 baseline  又和父级元素底边有一定的距离(这个距离和  font  有关,不一定是  5px ),所以以上代码的效果中不同 div  之间有间隙,这是因为图片与父元素的底边有距离。
(1) img 标签转化为块级元素。
(2) 通过 float 浮动解决。
(3) 父容器的字号设置为 font-size:0; 子元素的字号重新设置。
(4) 更改图片的对齐方式 vertical-align:top img{vertical-align:top}
18 JavaScript 事件的捕获和事件冒泡
DOM  事件流存在三个阶段:捕获阶段、目标阶段、冒泡阶段。

事件捕获:当鼠标点击或者触发 DOM 事件时(被触发 DOM 事件的这个元素被称为事件源),浏览器会从根节点  => 事件源(由外到内)进行事件传播。

事件冒泡:事件源 => 根节点(由内到外)进行事件传播。 

 DOM  标准事件流的触发的先后顺序为:先捕获,再冒泡。即当触发  DOM  事件时,会先进行事件捕获,捕获到事件源之后通过事件传播进行事件冒泡。
// 事件监听  addEventListener  方法:
// 第一个参数表示事件名称;
// 第二个参数触发事件后要执行的函数;
// 第三个参数默认值 false 表示事件冒泡阶段调用事件处理函数;true 表示事件捕获阶段调用事件处理函数。
element.addEventlistener(event,function,useCapture)
18.1 事件冒泡实例
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>事件冒泡</title>
    <style>
        #parent{
            width:200px;
            height: 200px;
            background: greenyellow
        }
        #child{
            width:100px;
            height: 100px;
            background:red
        }
    </style>
</head>
<body>
    <div id="parent">
      父元素
      <div id="child">
        子元素
      </div>
    </div>
    <script type="text/javascript">
      var parent = document.getElementById("parent");
      var child = document.getElementById("child");
  
      document.body.addEventListener("click",function(e){
        console.log("click-body");
      },false);
  
      parent.addEventListener("click",function(e){
        console.log("click-parent");
      },false);
  
      child.addEventListener("click",function(e){
        console.log("click-child");
      },false);
    </script>
  </body>
</html>
// 当点击子元素时,执行结果: click-child  click-parent  click-body
// 当点击父元素时,执行结果: click-parent click-body
// 事件执行顺序是:从 事件源 ==> 根节点。 
事件执行顺序是由内到外的,这就是事件冒泡。如果点击子元素不想触发父元素的事件,可使用  event.stopPropagation()  来阻止事件冒泡。
child.addEventListener("click",function(e){
    console.log("click-child");
    e.stopPropagation(); // 阻止事件往外冒泡。
})
18.2 事件捕获实例
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>事件冒泡</title>
    <style>
        #parent{
            width:200px;
            height: 200px;
            background: greenyellow
        }
        #child{
            width:100px;
            height: 100px;
            background:red
        }
    </style>
</head>
<body>
    <div id="parent">
      父元素
      <div id="child">
        子元素
      </div>
    </div>
    <script type="text/javascript">
      var parent = document.getElementById("parent");
      var child = document.getElementById("child");
      // false 表示在事件冒泡阶段,调用事件处理函数。
      // true 表示在事件捕获阶段,调事件处理函数。
      document.body.addEventListener("click",function(e){
        console.log("click-body");
      },false);
  
      parent.addEventListener("click",function(e){
        console.log("click-parent-事件传播");
      },false);

      parent.addEventListener("click",function(e){
            console.log('click-parent-事件捕获')
      },true)
  
      child.addEventListener("click",function(e){
        console.log("click-child");
        
        // 阻止事件冒泡
        //e.stopPropagation();
      },false);
    </script>
  </body>
</html>
// 当点击子元素时,(先捕获再冒泡)
// 执行结果是: click-parent-事件捕获   click-child   click-parent-事件传播  click-body

//父元素通过事件捕获的方式,注册了click事件,所以在事件捕获阶段就会触发,然后到了目标阶段,即事件源,
//之后进行事件冒泡,parent同时也用冒泡方式注册了click事件,所以这里会触发冒泡事件,最后到根节点
//(body)。这就是整个事件流程。(捕获阶段  ==>  目标阶段  ==>  冒泡阶段  ==>  根节点)
18.3 事件委托(事件代理)
事件委托也可以叫事件代理,是事件冒泡与事件捕获的运用。

概念:会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件相应到需要绑定的元素上时(事件捕获),会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上执行函数。优点:减少内存消耗,节约效率。
// 实例
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>事件委托</title>
    <style>
        ul li{
            margin-bottom: 20px;
            background: red;
        }
    </style>
</head>

<body>
    <ul id="list">
        <li class="class-1">item 1</li>
        <li>item 2</li>
        <li class="class-1">item 3</li>
        <li>item 4</li>
        <li>item 5</li>
        <li class="class-1">item 6</li>
    </ul>
</body>
<script>
    document.getElementById('list').addEventListener('click', function (e) {
        // 兼容性处理
        var event = e || window.event;
        // 获取目标元素
        var target = event.target || event.srcElement;   
        // 通过类名,判断哪个元素被触发了。
        if (target.className === 'class-1') {
            console.log('the content is: ', target.innerHTML);
        }
    });
</script>
</html>
19. JavaScript 中哪些是传数值,哪些是传地址
在  JavaScript  中,有两个不同的方式可以操作数据的值,分别是 传值 和 传址。

根据操作数据方式的不同,数据类型分为两种类型:基本数据类型 和 引用数据类型。

基本数据类型有:数字( number )、布尔类型( boolean )、字符串( string )。其操作方式为, 传数值。

引用数据类型有:对象( object )、数组( array )、函数( function )。其操作方式为, 传地址。
20 如何实现一个定时器
 function playTime(time){
    // setInterval 设置定时器。
    var stopTime = setInterval(function(){
        console.log(time)
        time--;
        if(time === -1){
            // clearInterval 关闭定时器
        	clearInterval(stopTime)
       }
    },1000)
}

//调用函数
playTime(15)
21 重绘与回流
回流:当渲染树(rander tree)中的一部分或者全部因为元素的尺寸,布局,隐藏等改变,而需要重新构建渲染树这个过程。回流一定引起重绘,重绘不一定引起回流。
// 触发回流的css属性
// 1. 盒子模型相关的
width,height,padding,margin,display,border-width,border,min-height

//定位属性以及浮动属性
position, top, left, right, bottom, float, clear

// 改变节点内部文字结构
text-align, overflow-y, font-weight, overflow, font-family, line-height, 
vertical-align, white-space, font-size。
重绘:当渲染树的一些元素需要更新属性,而这些属性影响外观,风格,样式,但不影响布局,这个重新渲染的过程。
// 触发重绘的属性
color, border-style, border-radius, visibility, background, text-decoration, outline
box-shadow
优化方法:

(1)避免使用触发重绘回流的  css  属性。

(2)将容易产生重绘的元素独立到一个图层。

(3)避免使用  table  布局,可能很小改动会造成整个  table  的重新布局。

(4)不要一条条的修改  DOM  样式,预先定义好  class ,然后修改  className 。
22 display : none , visibility:hidden 和 opacity:0 之间的区别
1. 空间占据:

 display:none  隐藏后不占据额外空间,它会产生回流和重绘。

 visibility:hiddle  和  opacity:0  元素虽然隐藏了,但仍占据空间,它们两个只会引起页面重绘。
2. 子元素继承

 display:none  不会被子元素继承。

 visibility:hidden  会被子元素继承,可以通过设置子元素  visibility:visible  使子元素显示出来。

 opacity:0  会被子元素继承,但是不能通过设置子元素  opacity:1  使子元素显示出来。
3. 事件绑定

 display:none  无法触发上面绑定的事件。

 visibility:hidden  无法触发上面绑定的事件。

 opacity:0  可以触发上面绑定的事件。
4. 过渡动画

 transition  对  display  是无效的。

 transition  对  visibility  也是无效的。

 transition  对  opacity  是有效的。 
23.1 Object.defineProperty() 方法使用。

 Object.defineProperty()  方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。
// 语法:
	Object.defineProperty(obj,propName,descriptor)

// 参数说明:
// obj:必需。目标对象。
// propName:必需的。需定义或者修改的属性的名字。
// descriptor:必需。目标属性所拥有的特性。
属性描述符是  Object.defineProperty(obj, propName, descriptor)  方法里的  descriptor  这个对象。属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可能是可写的,也可能是不可写的。存取描述符是由 getter-setter函数对描述的属性。属性描述符必须是这两种形式之一;不能同时是两者。
// (1)数据描述符
let obj = {}
object.defineProperty(obj,"name",{
    // enumerable:true时,该属性才能够出现在对象的枚举属性中。
    enumerable:false, //是可选值,默认值是 false
    // configurable 该属性的作用是:目标属性是否可以 delete 删除,是否可以再次设置特性。
    configurable:false, //是可选的,默认值是 false。
    // 当 writable:true 时,value 才能被赋值运算符改变。默认值为 false。 
    writable:false,
    // value 对应的是数字,可以是任何有效的 javaScript 数据类型。
    value:"张三"
})
// (2)存取描述符
let obj = {}
let newVal;
Object.defineProperty(obj,"name",{
    enumerable:false,
    configurable:false,
    get:function(){
       // 当获取值的时候,触发函数。
        console.log("读取数据时,执行里面的代码。")
        return newVal
    },
    set:function(value){
        // 当设置值的时候,触发函数,设置的新值通过参数 value 拿到。
        console.log("写入数据时,执行里面的代码。",value)
    }
})
存取描述符有  set  和  get  函数,但是不存在  writable  和  value  这2个属性。

 get  : 一个给属性提供  getter  的方法,如果没有  getter  则为  undefined  。当访问该属性时,该方法被执行,方法执行是没有参数传入,但是会传入 this 对象。(由于继承关系,这里的  this  并不一定是定义该属性的对象)。默认值为  undefined 。

 set  : 一个给属性提供  setter  的方法,如果没有  setter  则为  undefined 。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。默认值为  undefined 。 
属性描述符可同时具有的键值
configurableenumerableValuegetset
数据描述符YesYesYesNoNo
存取描述符YesYesNoYesYes
23.2 Vue的双向数据绑定原理

 vue.js  是采用数据劫持结合发布者-订阅者模式的方式,通过  Object.defineProperty()  来劫持各个属性的  setter  和  getter 。在数据变动时,发布消息给订阅者,触发相应的监听回调。

具体步骤:

第一步:需要数据监听器(Observe) 对数据对象进行递归遍历,包括子属性对象的属性,都加上 setter 和getter这样的话,给这个对象的某个值赋值。就会触发 setter,那么就能监听到了数据变化。

第二步: compile  解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图。

第三步:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁,主要做的事:(1)在自身实例化时,往属性订阅(dep)里面添加自己;(2)自身必须有一个 update() 方法;(3)待属性变动 dep.notice()通知时,能调用自身的 update() 方法,并触发 Compile 中绑定的回调。

第四步:MVVM 作为数据绑定的入口,整合 Observer、Compile 和 Watcher 三者,通过 Observe 来监听自己的 modes数据变化,通过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 Observer 和 Compile 之间的通信桥梁,达到数据变化 ->  视图更新;视图交互变化 -> 数据 model 变更的双向绑定效果。
// 简单的双向数据绑定实例
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>通过object.difinePorperty实现简单的数据双向绑定</title>
</head>
<body>
    <input type="text" id="input_id">
    <p id='p_id'></p>    
</body>
<script>
    var obj = {}
    var inputId = document.getElementById("input_id")
    var pId = document.getElementById("p_id")
    
    inputId.addEventListener('keyup',function(e){
        obj.name = e.target.value
    })
    Object.defineProperty(obj,'name', {
        get:function(){
            // 获取对象属性值的时候,执行里面的代码。
            console.log(1111)
        },
        set:function(value){
            // 设置对象属性值的时候,执行里面的代码。
            inputId.value = value
            pId.innerHTML = value
        }
    })
</script> 
</html>
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值