常见的前端面试题

Html5

新标签:

  • <header>定义文档的页眉,头部信息</header>

  • <nav>定义导航连接的部分</nav>

  • <footer>定义文档的页脚,底部信息</footer>

  • <article>内容区域</article>

  • <section>区块</section>

  • <aside>定义其当前所处内容以外的内容,侧边栏</aside>

  • 音频标签/(视频标签video)<audio src="" controls autoplay loop></audio> autoplay 自动播放 controls 是否显示播放控件 loop 循环播放 多浏览器支持方法:<audio><source src=“"></source></audio>

新属性 :

  • Placeholder:默认值、autofocus自动获取焦点、autocomplete自动完成、multiple多文件上传、required必填

  • type属性:email、tel、number、url、date、search(搜索框)、range(自由拖动的滑块)

怎么理解语义化标签 ????

  • 正确的标签做正确的事情,无CSS样式时也容易阅读,便于阅读维护和理解

  • 页面内容结构化 

  • 便于浏览器、搜索引擎解析。 利于爬虫标记、利于SEO搜索引擎优化

Css 

盒子模型 :

  • 标准盒模型:box-sizing:content-box;width(content)

  • 怪异盒模型:box-sizing:border-box;width(content+padding+border)

flex弹性布局 :

  • flex子项目在主轴的缩放比例 :flex: 2; 约束份数,整数值(使用后宽高失效). 如果用百分数,要加起来是100才可以用. 可实现圣杯布局

  • flex-direction调整主轴方向:row 、row-reverse、 column 、column-reverse

  • flex-wrap控制是否换行: nowrap、wrap、wrap-reverse;

  • flex-flow是flex-direction、flex-wrap的简写形式

  • align-items调整侧轴对齐(垂直对齐)(单行)stretch; 默认值。项目被拉伸以适应容器。center; 项目位于容器的中心。flex-start; 项目位于容器的开头。垂直对齐开始位置 上对齐、 flex-end; 项目位于容器的结尾。垂直对齐结束位置 底对齐

  • align-content堆栈(由flex-wrap产生的独立行)多行垂直对齐方式齐

    • stretch; 默认值。项目被拉伸以适应容器。center; 项目位于容器的中心。flex-start; 项目位于容器的开头。flex-end; 项目位于容器的结尾。 space-between; 项目位于各行之间留有空白的容器内。(中间空白)、space-around 项目位于各行之前、之间、之后都留有空白的容器内。(空白包裹项目)

    • 必须设置以下属性才会起作用。对父元素设置自由盒属性display:flex;设置排列方式为横向排列flex-direction:row;设置换行flex-wrap:wrap;

  • align-self自己的对齐方式;

  • justify-content调整主轴对齐(水平对齐)

  • order控制子项目的排列顺序:用整数值来定义排列顺序,数值小的排在前面。可以为负值。 默认值是 0

布局方式 :

垂直居中 :

已知盒子宽高: w100px;h100px

  • position:absolute;left: 50%; top : 50%; margin-top: -50px; margin-left: -50px;   定位之后需要退回盒子高宽一半的距离。

  • 利用 margin:auto 属性。  position:absolute; margin:auto;  top:0; left:0; right:0; bottom:0;

未知盒子宽高:

  • transform:translate(x,y);  将margin换为—》transform: translate(-50%, -50%);

  • flex弹性布局 父元素设置: display: flex; justify-content: center; align-items: center;

左列定宽,右列自适应 :

使用flex实现 :.parent{display:flex;} .left{width:100px;} .right{flex:1;} 

利用float+margin实现 :.left{float:left;width:100px;} .right{margin-left:100px;} 

使用float+overflow实现 .left{width:100px;float:left;} .right{overflow:hidden;}.  overflow:hidden,触发bfc模式,浮动无法影响,隔离其他元素

两侧定宽,中栏自适应 :

利用flex实现 : .parent{display:flex;} .left{width:100px;} .center{flex:1;}

利用float+margin实现 :  .left{width:100px;float:left;} .right{width:100px;float:right;}  .center{margin-right:200px;margin-left:200px;} 

两列定宽,一列自适应 : (基本html结构为父容器为parent,自容器为left,center,right.其中,left,center定宽,right自适应 )

利用flex实现: .parent{display:flex;} .left,.center{width:100px;} .right{flex:1}

利用float+margin实现 : .left,.center{float:left:width:100px;} .right{margin-left:200px;}

利用float+overflow实现 : .left,.center{float:left:width:100px;} .right{overflow:hidden;}

定位 :

静态定位:position:static;静态位置就是各个元素在HTML标准流中默认的位置。

相对定位:relative不脱标、元素相对于它在标准流中的位置进行定位、在标准流中的位置仍然保留。

绝对定位:absolute 脱离标准流,不占空间、找到最近的有定位(非static)的父元素才相对移动、子绝父相

块级元素、行内元素、行内块元素 :

常见的块元素:<h1>~<h6>、<p>、<div>、<ul>、<ol>、<li>等,其中<div>标签是最典型的块元素。

块级元素的特点:

  • 独占一行

  • 宽度默认是容器的100%

  • 高度,行高、外边距以及内边距都可以控制。

  • 可以容纳内联元素和其他块元素。(理解为可以容纳所有)

  • 注意 : p 里面不能放块级元素,同理还有这些标签h1,h2,h3,h4,h5,h6,dt,他们都是文字类块级标签,里面不能放其他块级元素。

常见的行内元素:<a>、<strong>、<b>、<em>、<i>、<del>、<s>、<ins>、<u>、<span>等,其中<span>标签最典型的行内元素。

行内元素的特点:

  • 和相邻行内元素在一行上

  • 默认宽度就是它本身内容的宽度。

  • 高、宽无效,但水平方向的padding和margin可以设置,垂直方向的无效。

  • 行内元素只能容纳文本或则其他行内元素。(a特殊)

  • 注意:链接里面不能再放链接。

常见行内块元素: <img />、<input />、<td>

行内块元素的特点:

  • 和相邻行内元素(行内块)在一行上,但是之间会有空白缝隙。

  • 默认宽度就是它本身内容的宽度。

  • 高度,行高、外边距以及内边距都可以控制。

选择器权重

!important              

内联样式(标签内style形式)

Id选择器

类选择器、属性选择器、伪类选择器

元素、伪元素选择器

通配符选择器 0

继承的权重最低

伪类选择器 :

链接伪类选择器:

  • link /* 未访问的链接 */

  • :visited /* 已访问的链接 */

  • :focus /* 聚焦状态 */

  • :hover /* 鼠标移动到链接上 */

  • :active /* 选定的链接 */

结构(位置)伪类选择器 :

  • :first-child

  • :last-child

  • :nth-child(n)

  • :nth-last-child(n)

目标伪类选择器:

  • :target 和锚点配合使用

px、em、rem的区别 :

  • px(像素 Pixel )相对长度单位。相对于显示器屏幕分辨率而言的。利用 px 设置字体大小及元素宽高等比较稳定和精确。Px 的缺点是其不能适应浏览器缩放时产生的变化,因此一般不用于响应式网站。

  • em 相对长度单位。相对于当前对象内文本的字体尺寸。如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸(一般为16px)。可以继承父元素尺寸大小。

  • rem : 1rem = html中font-size大小

浮动

  • 浮动元素特性:

    • 浮动元素元素会具有行内块元素的特性。

    • 浮动脱离标准流,不占位置,会影响标准流。浮动只有左右浮动。

    • 浮动的元素总是找离它最近的父级元素对齐。但是不会超出内边距的范围。

    • 元素的大小完全取决于定义的大小或者默认的内容多少.

  • 浮动的元素排列位置:

    • 浮动的元素顶端对齐,浮动元素不会和行内级元素层叠.

    • 上一个元素是(标准流)块级元素:则A元素会在他的下一行.

    • 上一个元素是(浮动)块级元素:则A元素会在他的同一行.

    • 上一个元素是行内元素或者行内块元素:会先找到父元素对齐,再和行内元素或者行内块元素混在一行显示

    • 上一行元素是纯文本:只会在当前行左右浮动,文字环绕

    • 下一个元素是纯文字:文字环绕

  • 清除浮动:清除浮动后造成的影响—》清除浮动主要为了解决父级元素因为子级浮动引起内部高度为0 的问题。

    • 额外标签法:在浮动元素末尾添加一个空的标签<div style="clear:both"></div>

    • 父级添加overflow属性方法:可以给父级添加:overflow: hidden|auto|scroll; 都可以实现。

    • 使用before和after伪元素清除浮动:

  • 固定定位:相对于浏览器视口。完全脱标,不占有位置,不随着滚动条滚动。

  • .clearfix::after{
        content:".";
        clear:both;
        display:block;
        height:0;
        visibility:hidden;
        /* line-height: 0px; */
    }
    
    .clearfix{ *zoom:1;}/* IE6、7 专有 */  由于IE6-7不支持:after,使用 zoom:1触发 hasLayout。

    bgc-属性:

  • background-color背景颜色red;#ff0000;rgb(255,0,0);rgba(255,0,0,0.3);transparent相当于rgba(0,0,0,0);完全透明; opacity:0-1之间取值

  • background-image: url(路径); 背景图片

  • background-repeat背景是否重复(是否平铺) repeat | no-repeat | repeat-x | repeat-y

  • background-position背景位置 可以是具体px 也可以是方位 -》background-position: 15px top; 

  • background-attachment背景是否滚动 scroll | fixed

  • background: 背景颜色 图片地址 是否重复 背景位置 是否滚动 ; 背景的复合属性

  • background-size背景缩放:设置长度单位(px)或百分比 、cover保证图片始终填充满背景区域 、contain保证图片

  • 多背景: 以逗号分隔可以设置多背景,设置的多重背景图之间存在着交集(即存在着重叠关系),前面的背景图会覆盖在后面的背景图之上。避免背景色将图像盖住,背景色通常都定义在最后一组上,

  • 渐变颜色background:linear-gradient:(角度,颜色 百分比,颜色 百分比);

浅谈javascript特性 :

  • 解析执行:轻量级解释型(不需要编译)的,或是 JIT 编译型(即时 实时 编译)的程序设计语言

  • 语言特点:动态语言,头等函数 (First-class Function)(又称函数是 JavaScript 中的一等公民)

  • 执行环境:在宿主环境(host environment)下运行,浏览器是最常见的 JavaScript 宿主环境,但是在很多非浏览器环境中也使用 JavaScript ,例如 node.js

  • 编程范式:基于原型、多范式的动态脚本语言(辅助语言),并且支持面向对象、命令式和声明式(如:函数式编程)编程风格

  • JavaScript 的组成 :

    组成部分

    说明

    Ecmascript

    描述了该语言的语法和基本对象

    DOM

    描述了处理网页内容的方法和接口

    BOM

    描述了与浏览器进行交互的方法和接口

JavaScript 执行过程 :

预解析

  • 全局预解析(所有变量和函数声明都会提前;同名的函数和变量,函数的优先级高)

  • 函数内部预解析(所有的变量、函数和形参都会参与预解析)

执行

先预解析全局作用域,然后执行全局作用域中的代码, 在执行全局代码的过程中遇到函数调用就会先进行函数预解析,然后再执行函数内代码。

事件冒泡、捕获、事件委托(代理):

事件捕获:结构上(非视觉上)嵌套关系的元素,会存在事件捕获功能,即同一事件,自父元素捕获至子元素(事件源元素)(自顶向下)

事件冒泡:结构上(非视觉上)嵌套关系的元素,会存在事件冒泡的功能,即同一事件,自子元素冒泡像父元素(自底向上);停止冒泡 :事件对象.stopPropagation();

DOM事件流的三个阶段是先捕获阶段,然后是目标阶段,最后才是冒泡阶段。

事件代理 : 就是利用事件冒泡机制 —》把一系列的内层元素事件 绑定到 外层元素。

可以通过 element.addEventListener() 设置一个元素的事件模型为冒泡事件或者捕获事件。

默认值false,表示事件冒泡;设为true时,表示事件捕获

数据类型 :

基本类型有6种,分别是undefined,null,bool,string,number,symbol(ES6新增) 

虽然 typeof null 返回的值是 object,但是null不是对象,而是基本数据类型的一种。

基本数据类型存储在栈内存,存储的是值。

复杂数据类型的值存储在堆内存,地址(指向堆中的值)存储在栈内存。当我们把对象赋值给另外一个变量的时候,复制的是地址,指向同一块内存空间,当其中一个对象改变时,另一个对象也会变化。

判断数据类型方法:

  • 简单数据类型: typeof

  • 复杂数据类型( array object ):

    • Instance of (不算最直接的判断方法)

    • is.array (es6里面最简单常用的是)

    • 原型链上tostring的方法。 通过Object下的toString.call()方法来判断。Object.prototype.toSring().call(arr)

    • 根据对象的contructor判断

解释 JavaScript 中的 null 和 undefined :

undefined 表示 声明没赋值;有容器没赋值

null表示一个空的对象,什么也没有。没容器

undefined是从null派生出来的

null == undefined //true

null === undefined //false

typeof null // 'object'

typeof undefined // 'undefined'

作用域和作用域链 : 

  • 作用域 :就是一个独立的空间,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。

    • 全局作用域:任何不在函数中或是大括号中声明的变量,都是在全局作用域下,全局作用域下声明的变量可以在程序的任意位置访问

    • 函数作用域:函数作用域也叫局部作用域,如果一个变量是在函数内部声明的它就在一个函数作用域下面。这些变量只能在函数内部访问,不能在函数以外去访问。

    • 块级作用域 :块级作用域可通过es6的let和const声明,所声明的变量在指定块的作用域外无法被访问。

  • 作用域链:当访问一个变量时,会先从本身作用域中去找这个变量,若找不到则向上一级作用域中去找,依次类推,就形成了一个作用域链。

变量声明提升  :

比如var a = 1; 未声明变量前,去使用变量,并不报错,而是打印undefined,将var a;变量提升了;

call、apply、bind区别:

  • call(对象, 参数1,2,…),换掉了this, 支持传参, 执行了方法, 只有当次调用有效

  • apply(对象, [1,2,3]);换掉了this, 参数以数组的形式传进去, 执了行方法, 只有当次调用有效

  • bind(对象, 参数1,2,..);换掉了this,不执行当前函数,以返回值的形式给出新的函数。  每次调用都有效

    es6 :

  • 创建变量 :var let const区别

    • var:有变量提升,没有块级作用域。

    • let:没有变量提升,必须先定义再使用。全局变量不会附加到window对象的属性中。不能重复定义。具有块级作用域

    • const :一旦声明立即初始化。没有变量提升,全局变量不会附加到window对象的属性中。具有块级作用域

      • const创建的常量什么能改变,什么不能改变????

      • const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。

    • let暂时性死区:在块级作用域内,没有声明变量前,变量是不可用的。

  • 解构赋值 :

    • 数组 : 

      • 变量个数等于数组长值

      • 变量个数大于数组长度: 有默认值使用默认值,无默认值undefined

      • 变量个数小于数组长度 : 忽略

      • 用空格跳过不需要的值

      • 剩余值 :扩展运算符,最后一个参数前面加三个…,可以获取传进来的多余的参数的数组

      • 复杂嵌套场景

    • 对象 :

      • (...)

      • 可以给变量起别名

  • 箭头函数 :

    • 写法简单:

      • 只有一个参数,可以省略()

      • 只有一条命令语句 ,可以省略{}

      • 只有一条命令语句 且是return,可以省略{}和 return

    • 与普通函数区别:

      • 没有arguments(伪数组),有扩展运算符得到的是真数组

      • this指向是当前作用域以外的那个作用域

      • 不能做构造函数

  • 扩展运算符 : 可以获取传进来的多余的参数的数组,与arguments(伪数组相比),扩展运算符得到的是真数组;

  • 字符串模版 : 反引号,${}为界定符号,里面可以使用变量,表达式;可以在里面换行

  • set对象 : let set = new Set() 数组去重  、for循环解决数组去重(两个for循环嵌套,I=0,i<arr.length; j<I+1;j<arr.lenght;arr[I]=arr[j]时;splice(j,1))

    Arr数组api : 不改变原数组 slice concat

  • push  在数组最后一个位置添加元素

  • pop 在数组最后一个位置删除一个元素,会修改数组的长度

  • shift 在数组最开始的位置删除一个元素;修改数组的长度

  • unshift  在数组最开始的位置添加一个元素;修改数组的长度

  • sort 排序

  • reserve 反转

  • splice 删除 替换 插入

  • slice 剪切 包左不包右. 不会修改原数组

  • split 替换字符串 返回数组

  • concat() 把两个数组拼接到一块,返回一个新数组 ;(arr.push()打印新的数组是二维数组);不改变原数组

  • arr.toString() 把数组转换为字符串,用逗号分隔

  • arr.indexOf() 找数组中某个数的索引 没找到返回-1 

  • arr.join() 替换分隔符

  • Array.isArray() 判断是不是数组

  • Array.from() 把其它非数组的对象转成数组。自定义对象、arguments 伪数组、dom操作拿到的NodeList集合

  • arr.find((item,index,arr)=>{}) arr.find找到数组中第一个满足条件的成员并返回该成员,如果找不到返回 undefined。

  • arr.findIndex((item,index,arr)=>{}) arr.findIndex找到数组中第一个满足条件的成员并返回该成员索引,如果找不到返回 -1。

  • arr.includes(必须->表示查找的内容,可选->表示开始查找的位置,0表示从第一个元素开始找。默认值是0。)   判断数组是否包含某个值

  • forEach()(回调函数,对象(this))

  • map() 返回值 组成数组

  • filter() 返回值的布尔类型是true 的 组成数组

  • some()  返回值有一个是true 就返回true

  • every()  返回值有一个是false 就返回false

  • reduce(参数一,参数二) 求数组的和

    • 参数一:函数遍历、function(preValue,n){};第一次遍历结果preValue为0,因为初始值为0 ;n为数组值;第二次遍历结果preValue为return返回值,n为数组值

    • 参数二:初始值,一般写0

闭包:

  • 闭包是(两个)函数嵌套,里面的函数用外面函数的变量,将里面函数暴露出来,(里面的函数重要),再调用

  • 闭包的原因:返回的函数并非孤立的函数,而是连同周围的环境(AO)打了一个包,成了一个封闭的环境包,共同返回出来 ---->闭包。我们在返回函数的时候,并不是单纯的返回了一个函数,我们把该函数连同他的AO链一起返回了。

  • 优点:缓存数据保数据的安全, 不会污染全局变量,形成块级作用域,变量私有化

  • 缺点 : 

    • 内部执行完,函数弹栈,一旦被清除,无法访问这些数据了。—》 js垃圾回收机制:js 中的变量和函数不再使用后,会被自动js垃圾回收机制回收。

    • 占用内存:内部套多层,要等到执行到最里面,才一层层弹栈.

  • 什么时候使用 :可以用来创建私有变量或函数

for循环里面绑定事件 形成闭包

解决办法1 : 用立即执行函数解决闭包

for(vari=0; i<10; i++){
        document.body.addEventListener('click',(()=> {
            console.log(i).  // 如果不用立即执行函数打印出10个10,用的话打印0123456789
        })(i))
    }

解决办法2 : ES6 let 块级作用域

 var liArr=document.querySelectorAll('li');
    for(leti=0; i<liArr.length; i++){
        liArr[i].onclick=function(){
            console.log(i);
        }
    }

原型、原型链 :

在每一个实例对象中的_proto_(原型),中同时有一个constructor(构造器)属性,该属性指向创建该实例的构造函数:

每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。

原型链思想:先在自己身上找,找到即返回,自己身上找不到,则沿着原型链向上查找,找到即返回,如果一直到原型链的末端Object.prototype.还没有找到,则返回 null。

js预编译

function test(){}
var test=new Function()
test.__proto__==  Function.prototype. // 构造函数创建的实例对象的都有__proto__指针指向构造函数的prototype
Function.prototype.__proto__=  Object.prototype 
// 空对象           Object的原型

new的过程  :     

  • 创建一个新对象 (开辟堆内存空间)   

  • 将构造函数的作用域赋给新对象(让内部的this 指向了这个新对象)(接下来所有针对 this 的操作实际上操作的就是 新对象)

  • 调用构造函数 (执行构造函数中的代码)

  • 返回新对象( 如果构造函数没有显式的返回值,隐式的返回this对象)

Js继承方式 :

借用构造函数继承,原型链继承,组合继承,原型式继承、寄生式继承,寄生组合继承、es6的类继承

借用构造函数继承 :在子类构造函数的内部调用父类构造函数 使用apply()或call 方法,换掉this对象

缺点 :只能继承父类的实例属性和方法,不能继承原型属性/方法

function Parent(name) {
    this.colors=["red","blue","green"];
    this.name=name;
}
function Sun(name,age) {
    // 关键点 继承 Parent 类 并传递了参数
    Parent.call(this,name,age)
    this.age=age;
}
var s1=new Sun('jack',18);// {colors: Array(3), name: "jack", age: 18}
console.log(s1);

原型链继承 :将别人的实例变为自己的原型。子类可以通过原型链的查找,实现父类的属性公用与子类的实例

缺点: 一些引用数据操作的时候会出问题,两个实例会公用继承实例的引用数据类

function Parent() {
    this.property=true;
    this.colors=["red","blue","green"];
}
Parent.prototype.getParentValue=function() {
    return this.property
}

function Sun() {
    this.sunProperty=false;
}
Sun.prototype=newParent(); // 关键 创建Sun的实例,并将该实例赋值给Sub.prototype
Sun.prototype.constructor=Sun;
Sun.prototype.getSunValue=function() {
    return this.subProperty
}
var s1=new Parent();
console.log(s1.getParentValue())//true
var s2=new Parent();
s2.colors.push("black");
console.log(s1.colors)// ["red", "blue", "green"]

组合继承:用原型链实现对原型属性和方法的继承,用借用构造函数技术来实现实例属性的继承。

缺点 :调用两次超类型, 存在两份相同的属性/方法,实例和原型上各有一份

function Parent(name) {
    this.colors=["red","blue","green"];
    this.name=name;
}
Parent.prototype.sayName=function() {
    console.log(this.name)
}

function Sun(name, age) {
    // 关键点 继承 属性
    Parent.call(this, name)//第二次调用
    this.age=age
}
//继承父类原型方法
Sun.prototype=newParent()//第一次调用
// 重写SubType.prototype的constructor属性,指向自己的构造函数SubType 否自指向超类型
Sun.prototype.constructor=Parent;
Sun.prototype.sayAge=function() {
    console.log(this.age)
}

var s1=new Sun('小明',20);
s1.colors.push("black");
console.log(s1.colors)// ["red", "blue", "green", "black"]
s1.sayAge()//20
s1.sayName()// 小明

原型式继承 :利用一个空对象作为中介,将某个对象直接赋值给空对象构造函数的原型。

缺点:不能做到函数复用、共享引用类型属性的值、无法传递参数

function object(obj) {// 空对象
    function F() { }
    F.prototype=obj;// 将某个 空对象 直接 赋值给空对象的构造函数的原型
    return new F();
}

var person={
    name:'Nicholas',
    friends:["Shelby","Coury","Van"]
}

var anotherPerson=object(person)
// 与Object.create 相同 相比之下 Object.create更规范化
// var anotherPerson = Object.create(person)
anotherPerson.name="Greg";
anotherPerson.friends.push("Rob");
console.log(anotherPerson);//__proto__:friends: (4) ["Shelby", "Coury", "Van", "Rob"]

寄生式继承 :在原型式继承的基础上,增强对象,返回构造函数 (原生式 + 工厂模式)

缺点 :(同原型式继承)

function object(obj) {
    function F() { }
    F.prototype=obj;    
    return newF();
}

function createAnother(original) {
    var clone=object(original);// 或 Object.create(original)
    clone.sayHi=function() {// 以某种方式来增强对象
        alert("hi");
    };
    return clone;// 返回这个对象
}    

var person={
name:'Nicholas',
friends:["Shelby","Coury","Van"]
}
var anotherPerson=createAnother(person)
anotherPerson.name="Greg";
anotherPerson.friends.push("Rob");
console.log(anotherPerson);// __proto__:friends: (4) ["Shelby", "Coury", "Van", "Rob"]

寄生组合式继承

结合借用构造函数传递参数和寄生模式实现继承

通过借用构造函数继承属性,通过原型链的混成形式继承方法

使用寄生式继承来继承父类的原型,然后在将结果指定给子类型的原型

function inheritPrototype(Sun, Parent) {
    var prototype=Object.create(Parent.prototype);// 创建对象,创建父类原型的一个副本
    prototype.constructor=Sun;// 增强对象,弥补因重写原型而失去的默认的constructor 属性
    Sun.prototype=prototype// 指定对象,将新创建的对象赋值给子类的原型
}

// 父类初始化实例属性和原型属性
function Parent(name) {
    this.name=name;
    this.colors=["red","blue","green"];
}
Parent.prototype.sayName=function() {
    alert(this.name);
};

// 借用构造函数传递增强子类实例属性(支持传参和避免篡改)
function Sun(name, age) {
    Parent.call(this, name);
    this.age=age;
}

// 将父类原型指向子类
inheritPrototype(Sun, Parent);
    Sun.prototype.sayAge=function() {
    alert(this.age)    
}

var instance1=new Sun("rose",22);
var instance2=new Sun("jack",23);
instance1.colors.push("black");// ["red", "blue", "green", "black"]
instance2.colors.push("gray");// ["red", "blue", "green", "gray"]

ES6类继承extends

extends关键字主要用于类声明或者类表达式中,以创建一个类,该类是另一个类的子类。

super调用父类属性、方法

class Parent{
    constructor(name, age) {
        this.age=age;
        this.mame=name;
    }
    getAge() {
        returnthis.age;
    }
}

class Sun extends Parent{
    constructor(name, age, sex) {
        //调用超类型 必须在使用“this”之前首先调用 super()。
        super(name, age)
        this.sex=sex
    }
}

var instance=new Sun('zxy',18,'女');
console.log(instance.getAge());

vuejs框架

 —》从定义入手,官方怎么定义的、我对他的理解是什么??

用于构建用户界面的渐进式javascript框架:易用、灵活、高效

具体怎样—》易用、灵活、高效 ???

vue:可以通过尽可能简单的 API ,列表渲染(v-for)、条件渲染(v-if)、事件渲染、data,components , props , computed , watch , methods , mixin ,filter

vue两大特点:

响应式编程、vue.js会自动对页面中 某些数据的变化做出响应。

组件化。将各种模块拆分到一个一个单独的组件(component)中,

组件化开发的优点:提高开发效率、方便重复使用、提升整个项目的可维护性、便于协同开发。

vue是单页面应用:使页面局部刷新,不用每次跳转页面都要请求所有数据和dom,这样加快了访问速度和提升用户体验。

优点:数据驱动、组件化、轻量、简洁

缺点:

  • 第一次加载首页耗时相对长一些;

  • 不可以使用浏览器的导航按钮需要自行实现前进、后退。

  • 不利于seo

而且他的第三方ui库很多节省开发时间。

生命周期 

定义:事物从诞生到消亡的整个过程

生命周期分为三大阶段 :初始化阶段、更新阶段、销毁阶段

初始化阶段的钩子函数:

  • beforeCreate()实例创建前:数据和模版均未获取到

  • created() 实例创建后:最早可以访问到data数据,但是模版未获取到

  • beforeMount() 数据挂载前:模版已经获取到,但是数据未挂载到模版上

  • mounted() 数据挂载后:数据已经挂载到模版中 

更新阶段的钩子函数:

  • beforeUpdata() 模版更新前:data改变后更新数据模版前调用

  • updated()模版更新后:将data渲染到数据模版中

销毁阶段的钩子函数:

  • beforeDestroy() 实例销毁前

  • destroyed() 实例销毁后

    v-model 双向绑定原理 :

    input,绑定input事件,当input发生改变时,获取event.target.value的值,赋值给数据,所以input改变时数据msg也发生改变了,v-bind动态绑定:value=msg;数据改变时,input可以发生改变。

    <div id="app">
        <input type="text"@input="fn":value="msg">
        {{msg}}
    </div>
    <script src="node_modules/vue/dist/vue.js"></script>
    <script>
        const app=newVue({
            el:'#app',
            data:{
            msg:''
            },
        methods:{
            fn(event){
                this.msg=event.target.value
            }
        }
    })
    </script>
    

    底层原理:

    observer监听$options,

    vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过**Object.defineProperty()**来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。

    vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。

    mvvm模型 :

    Model:模型, 数据对象(data选项当中的)

    View:视图,模板页面(用于渲染数据)

    ViewModel:视图模型,其实本质上就是new的 Vue 实例。里面有一个dom监听器,当监听到view试图发生改变时,data中的数据更新

    将model层中的数据绑定到view层 。通过**Object.defineProperty()数据劫持,

    组件:组件传值:

  • 父传子:props

    • 父组件动态绑定自定义属性为要传给子组件的数据,子组件props接收传过来的数据,props可以是数组,可以是对象,对象时,可以设置属性,传输类型、必填、默认值。

  • 子传父:$emit

    • 定义一个事件,来触发this.$emit(),将子组件的数据发送给父组件,this.$emit(),参数一自定义事件,参数二发送的数据。父组件绑定自定义事件,相应的回调函数可以使用传过来的数据

    • 作用域插槽实现子传父:—》使用子组件中的数据,填充到父组件插槽标签中,子组件插槽中:data=“msg”,父组件插槽中,template模版<template slot-scope=“slot”><template>,可以用slot.data获取子组件数据了

    • (具名插槽 :子组件使用slot标签,属性name给插槽命名,父组件可以通过子组件的name具体更改摸一个插槽。)

  • 兄弟之间:

    • vuex状态管理模式 : 将数据存到vuex的state里面,全局可以使用

    • 插件PubSub :发送数据PubSub.public(事件名称,数据),接收数据PubSub.subscribe(事件名称,(event,data)=>{})

    • 子传父父传子 

  • 父组件和子组件的v-model

    • 父组件将数据传给子组件,子组件props接收数据

    • 子组件中:v-model不能绑定props属性,

    • 子组件自己的data 是个函数,返回对象,v-model应绑定子组件中的data,实现副组件数据发生改变,子组件也改变

    • 子组件数据改变时,触发事件,利用子传父 $emit 将数据值传给父组件,实现父子组件的双向绑定

      $refs 和 $el的用法 :

    • ref 加在普通的元素上,用this.$refs(ref值),获取到的是dom元素。

    • ref 加在子组件上,用 this.$refs 获取到的是组件实例,可以使用组件的所有方法。在使用方法的时候直接this.$refs.XX。

    • vm.$el:获取Vue实例关联的DOM元素

      nextTick 是做什么的 

      $nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的DOM。

      computed、watch、method :

      Computed:属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;本质是getter、setter,默认只有get,getter方法,获取数据,也可以使用set方法改变数据;

      (比如输入框,双向绑定计算属性,当输入框发生改变时,计算属性不会改变,只有设置了set函数 才发生改变,参数newValue)

      Methods:方法表示一个具体的操作,主要书写业务逻辑;

      Watch:主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作。当属性数据发生变化时,对应属性的回调函数会自动调用, 在函数内部进行计算。通过watch选项 或者 vm 实例的$watch()来监听指定的属性

      Computed好处:在模板中放入太多的逻辑会让模板过重且难以维护,在需要对数据进行复杂处理,且可能多次使用的情况下,尽量采取计算属性的方式。好处:

    • 使得数据处理结构清晰;

    •   依赖于数据,数据更新,处理结果自动更新;

    • 在template调用时,直接写计算属性名即可;

    • 常用的是getter方法,获取数据,也可以使用set方法改变数据;

    • 相较于methods,不管依赖的数据变不变,methods都会重新计算,但是依赖数据不变的时候computed从缓存中获取,不会重新计算

    • 计算属性内部this指向vm实例

自定义指令directive:

directives : { 
  '指令名' : { // 指令名不要带 v-
    bind (el, binding){// 指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。(css样式)
    },
    inserted (el, binding) {.   //.   inserted: 当被绑定的元素插入到 DOM 中时(js代码)
      // 逻辑代码
      // el 代表使用了此指令的那个 DOM 元素
      // binding 可获取使用了此指令的绑定值 等 binding.value   binding.name
    }
  }
}

filter过滤器 : 

Filters是个对象,过滤器名称,对应回调函数,参数value,返回对value进行的操作。可以用在双花括号{{}}和v-bind表达式

 // 局部 过滤器
        filters : {              
            a (value){
                if(!value) {
                    return
                }
                returnvalue.replace('sb','**')
            }
        }

事件修饰符:

  • .prevent阻止默认行为

  • .stop阻止事件冒泡

  • .enter监听特殊按键

  • .once事件只调用一次

  • .self: 当事件发生在该元素本身而不是子元素的时候会触发

  • .capture: 事件侦听,事件发生的时候会调用;

组件中的data为什么必须是一个函数?

如果组件内部是data对象,会造成数据污染,组件实例共用一个data数据,一个改变,会影响其他连琐反应,当组件内部data属性是一个函数时,不会造成数据污染,比如一个函数,他的每个实例对象,的对象不同,改掉其中一个,不会引起连锁反应。

vue-router:

hash路由 和history的 :

  • location.hash = “foo"

  • history.pushState(data, title,url)  可以实现不刷新页面改变url,可以利用history.back() 返回上一个路由.

    • data用于存储自定义数据,通常设为null

    • title网页标题,基本上没有被支持,一般设为空

    • url 以当前域为基础增加一条历史记录,不可跨域设置

  • histoty.replaceState(data, title, url)  只是替换当前url,不会增加/减少历史记录。

  • onpopstate事件,当前进或后退时则触发

Router-link:

属性:to跳转的路径、tag渲染成什么标签、active-class 更改class属性名

route 和router :

$router 为 VueRouter 实例,想要导航到不同 URL,则使用 $router.push 方法

$route 为当前活跃的路由,$route可以获取 name 、 path 、 query 、 params 等

嵌套路由:

配置路由映射:children:[],path、components,path可以是相对路径,也可以是绝对路径

路由传参:

params:配置路由映射,path: ‘/home/:id’,router-link标签动态绑定to属性,to :’/home/id’,在路由出口可以使用$route.params获取参数

query:配置路由映射,path: ‘/home’,router-link标签动态绑定to属性,to :’{path:’/home’,query:{name:a, age:10}}’,在路由出口可以使用$route.query获取参数

keep-alive:

用于保留组件状态或避免重新渲染。keep-alive标签将router-view标签包裹

路由守卫beforeEach  

实现meta的title ——》点击切换不同路由时,title随着对应切换

// 在index.js文件中  配置路由映射中
constrouter=new VueRouter({
  routes : [
    {
      path : '/home',
      component : Home,
      meta : {                // 元数据:描述数据的数据
        title: '首页'
      }
    }
  ]
})

// 全局导航守卫 !!!!!!1
// 前置钩子。在路由跳转之前实现的回调
router.beforeEach((to, from, next) => {
  document.title=to.matched[0].meta.title; // !!!!!!!
  console.log(to)
  next()
})

vuex :

  • vuex是专门为vue.js提供的一种状态管理模式,它采用的是集中式储存和管理所有组件的状态和数据,方便使用。

  • vue组件—》dispatch调用actions--〉actions context.commit调用mutations--》mutations 改变state里的数据。

  • state 用来存放全局数据

  • mutations 用来修改state中的数据

  • actions 用来调用异步方法

  • modules 当项目很大时,将每个模块抽离出来

  • getters 派生属性: 类似vue的计算属性,主要用来过滤一些数据。

vue数据流?子组件为什么要把事件的一个状态的变更 提交一个事件上去 交由父组件去修改 子组件为什么不能直接改props ?

Vue 在数据操作上支持单向绑定和双向绑定:(单双向绑定:指的是 view层和 model 层之间的映射关系)

  • 单向绑定:例如 Mustache 插值语法,v-bind 等;

  • 双向绑定:即表单的 v-model。它实际上是一个语法糖,背后包括两步操作:

    • v-bind:value:model 层的更改同步到 view 层

    • v-on:input:view 层的更改同步到 model 层

      单向数据流:数据流,指的是组件之间的数据流动;

      虽然 v-model 是双向绑定,但 Vue 实际上是单向数据流。 假设想要用子组件的 prop 做一个双向绑定,那么我们的代码可能会这么写:

      父组件的数据动态绑定自定义属性,传送到子组件,子组件通过props接收,v-model双向绑定props接受的数据。

      我们会发现 model 层的确随着 view 层同步改变了,但是控制台里会报错:

      因为 Vue 是单向数据流,数据总是从父组件传到子组件,子组件没有权利修改父组件传过来的数据,只能请求父组件对原始数据进行修改。防止从子组件意外改变父级组件的状态,从而导致应用的数据流向难以理解

      在开发中可能有多个子组件依赖于父组件的某个数据,万一子组件真的可以直接修改父组件数据,那么一个子组件变化将会引发所有依赖这个数据的子组件发生变化,所以 Vue 不推荐子组件修改父组件的数据,直接修改 prop 会抛出警告。

      <div id="app">
          <cpn v-bind:value2="value"></cpn>
      </div>
      <template id="cpn">
          <input type="text"v-model="value2">
          <h2>{{value2}}</h2>
      </template>
      
      const cpn={
          template:"#cpn",
          props:["value2"]
      }
      const app=new Vue({
          el:'#app',
          data:{
          value:0
      },
      components:{
          cpn
          }
      })

但是,很多时候我们又确实要操作这个数据,那么应该怎么办呢? 有两种方法:

定义一个局部变量,并用 prop 的值初始化它:相当于创建了原始 prop 的副本了,之后怎么操作数据都是操作的子组件数据,不会影响到父组件数据;

props: ['initialCounter'],
data:function() {
    return{
        counter:this.initialCounter
    }
}

定义一个计算属性,处理 prop 的值并返回:注意 trim() 会返回一个处理完成后的新字符串,同样不会影响到父组件数据(原字符串)。

props: ['size'],
computed: {
    normalizedSize:function() {
        return this.size.trim().toLowerCase()
    }
}

之后如果父组件确实要用到这个处理后的值,就通过 $emit 的方式传给父组件即可。

拿前面的例子来说,我们想要利用 prop 这个数据实现双向绑定,可以这么写:

<div id="app">
    <cpn v-bind:value2="value"></cpn>
</div>
<template id="cpn">
    <input type="text"v-model="value3">
    <h2>{{value3}}</h2>
</template>

const cpn={
    template:"#cpn",
    props:["value2"],
    data:{
        value3:this.value2
    }
}
const app=new Vue({
    el:'#app',
    data:{
        value:0
    },
components:{
      cpn
    }
})

注意:在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。 比如下面这段代码:

<div id="app">
    <h2>父组件数据:{{parent}}</h2>
    <cpn v-bind:obj1="parent"></cpn>
</div>
<template id="cpn">
    <div>
        <h2>子组件数据:{{son}}</h2>
        <input type="text"v-model="son.age">
    </div>
</template>

<script>
const cpn={
    template:"#cpn",
    props:["obj1"],
    data() {
        return{
            son:this.obj1
        }
    }
}
const app=newVue({
    el:'#app',
    data:{
        parent:{age:20}
    },
    components:{
        cpn
    }
}
</script>

这里的 this.obj1 是引用,赋值给了 son,所以 son 实际上还是指向了父组件的数据,对 son.age 的修改依然会影响到父组件,如图:

所以,我们实际上需要的是一个对象副本。因为对象属性都是基本类型,这里只用浅拷贝即可(如果对象属性还是对象,就得用深拷贝):

const cpn={
    template:"#cpn",
    props:["obj1"],
    data() {
        return{
            // son:this.obj1
            son:Object.assign({},this.obj1)
        }
    }    
}

Vue中v-if和v-show的区别?

  • 渲染 :

    • v-show : 浏览器首先什么都不管,先把HTML元素先渲染起来,符合条件就显示,不符合条件display就为none,不显示,但是元素还在.

    • v-if : 浏览器先判断符不符合条件,符合了再渲染,否则不渲染DOM,浏览器中找不到这个DOM。

  • vue生命周期区别 :

    • v-if由于是重新渲染,所以每次切换一次会重新走一次生命周期

    • v-show由于只是控制显示隐藏,所以除了初始化渲染,其他时候都不会再走相关生命周期了。

  • 性能区别 : 

    • v-if有更高的切换开销,v-show有更高的初始渲染开销。如果需要频繁的切换,使用v-show比较好,如果运行条件很少改变,使用v-if比较好。

    • v-show比v-if性能更高,因为v-show只能动态的改变样式,不需要增删DOM元素。所以当程序不是很大时候,v-if和v-show区别都不大,如果项目很大,推荐多用v-show,较少浏览器后期操作的性能。

后续更新react.......

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值