前端 面试题集合

131 阅读17分钟

css 样式

css 有哪些选择器

1)id选择器(#myid)

2)类选择器(.myclass)

3)标签选择器(div,h1,p)

4)相邻兄弟选择器(h1+p)

5)子代选择器(ul>li)

6)后代选择器(li a)

7)全局选择器(*)

8)属性选择器(a[rel="external"])

9)伪类选择器(a:hover,li:nth-child)

块元素和行内元素的区别,如何相互转换

一般通过display属性来区分块级元素和行内元素,block代表块级元素,inline代表行内元素。 块级元素: 1、内容独占一行。 2、width和height可以设置。 3、margin和padding也可以设置。 行内元素: 1、内容不独占一行。 2、width由内容撑开,不可设置。 3、竖直方向marigin和padding不可设置,水平方向可设置。 4、有多个行内元素,则会排列在一行,直到父容器放不下才会换行。 块级元素和行内元素通过修改display属性可相互切换。

常用的ui组件有哪些

css如何让一个元素水平垂直居中

<div class="par">
    <div class="child"></div>
</div>

1、弹性盒

    .par{
        width: 600px;
        height: 600px;
        background-color: red;
        display: flex;
        justify-content: center;
        align-items: center;
    }
    .child{
        width: 200px;
        height: 200px;
        background-color: pink;
    }

2、绝对定位

 .par{
        width: 600px;
        height: 600px;
        background-color: red;
        position: relative;
    }
    .child{
        width: 200px;
        height: 200px;
        background-color: pink;
        position: absolute;
        top: 50%;
        left: 50%;
        margin-left: -100px;
        margin-top: -100px;
    }

3、表格法

.par{
        width: 600px;
        height: 600px;
        background-color: red;
        display: table-cell;
        vertical-align: middle;
    }
    .child{
        width: 200px;
        height: 200px;
        background-color: pink;
        margin: 0 auto;
    }

4、margin计算

.par{
        width: 600px;
        height: 600px;
        background-color: red;
        overflow: hidden;
    }
    .child{
        width: 200px;
        height: 200px;
        background-color: pink;
        margin: 200px auto;
    }

HTML5新增的元素

首先html5为了更好的实践web语义化,增加了header,footer,nav,aside,section等语义化标签,在表单方面,为了增强表单,为input增加了color,emial,data ,range等类型,在存储方面,提供了sessionStorage,localStorage,和离线存储,通过这些存储方式方便数据在客户端的存储和获取,在多媒体方面规定了音频和视频元素audio和vedio,另外还有地理定位,canvas画布,拖放,多线程编程的web worker和websocket协议

javascript

代码实践01

	var scope = 'glo';
	function fn(params) {
	    console.log("01",scope);
	    var scope = "local";
	    console.log("02",scope);
	}
	fn();

![代码实践结果01](/Users/jing/Library/Application Support/typora-user-images/image-20210222133711564.png)

代码实践02

for (let i = 0; i < 5; i++) {
	(function(i){
		setTimeout(function(){
			console.log("03",i);
		})
	})(1000*i);
}

![image-20210222134322713](/Users/jing/Library/Application Support/typora-user-images/image-20210222134322713.png)

统计字符串中出现字符最多的算法

var str = 'abdgdbca';
	function fn(str){
		let dic = {};
		for (var i =  0; i < str.length; i++) {
			if(dic[str[i]]){
				dic[str[i]]++;
			}else {
				dic[str[i]] = 1;
			}

		}
		console.log("04",dic);
	}
	fn(str);

![image-20210222140057880](/Users/jing/Library/Application Support/typora-user-images/image-20210222140057880.png)

数字千位分隔符的实现 ***

function numFormat(num){
    num=num.toString().split(".");  // 分隔小数点
    var arr=num[0].split("").reverse();  // 转换成字符数组并且倒序排列
    var res=[];
    for(var i=0,len=arr.length;i<len;i++){
      if(i%3===0&&i!==0){
         res.push(",");   // 添加分隔符
      }
      res.push(arr[i]);
    }
    res.reverse(); // 再次倒序成为正确的顺序
    if(num[1]){  // 如果有小数的话添加小数部分
      res=res.join("").concat("."+num[1]);
    }else{
      res=res.join("");
    }
    return res;
}

var a=1234567894532;
var b=673439.4542;
console.log(numFormat(a)); // "1,234,567,894,532"
console.log(numFormat(b)); // "673,439.4542"

解析url对象,并返回相关参数名称和参数值

function sys_getUrlkey(url) {
  var params = {};
  try{
    var urls = url.split("?");                  
    console.log('1_分割url:', urls)
    var arr = urls[1].split("&");               
    console.log('2_分割urls[1]:', arr)
    for (var i = 0, l = arr.length; i < l; i++) {
      var a = arr[i].split("=");                
      console.log('3_遍历 arr 并分割后赋值给a:', a[0], a[1])
      params[a[0]] = a[1];                      
      console.log('4_a给params对象赋值:', params)
    }
                                            
    console.log('5_结果:', params)
  }catch(e){}
  return params;
}

给出数组,向数组中指定位置插入指定元素

var myArr = [3,4,5,7,8,21,32,22,2]
var elementy = 5; // 需要插入的元素
var index = 6 ; // 需要插入元素所在的索引值
myArr.splice(index, 0, elementy);

给出指定的时间,计算当前时间到指定时间的剩余总天数

var eneTime = new Data("2019/8/7")

获取所有属性值,拼接成字符串

var person = {fname:'john',age:18}
var str = "";
for(let key in person){
    str+=key+'='+ person[key]+","; 
}
console.log(str.substring(0,str.length-1));
// fname=john,age=18

使用setTimeout实现一个debounce函数

function debounce(fn, delay) {
  let timer
  return function(...args) {
    if (timer) clearTimeout(timer)
    // 使用箭头函数来处理this问题
    timer = setTimeout(() => fn.apply(this, args), delay)
  }
}

闭包

一句话可以概括:闭包就是能够读取其他函数内部变量的函数,或者子函数在外调用,子函数所在的父函数的作用域不会被释放。

什么是闭包(closure),为什么使用闭包?

闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部。

方法a 嵌套方法b 想要调用b的变量需要b变量return出去。

作用:

1.内部函数可以引用外层的参数和变量 2.参数和变量不会被垃圾回收机制回收

什么是闭包

  “官方”的解释:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函 数),因而这些变量也是该表达式的一部分。 通俗的讲就是函数a的内部函数b,被函数a外部的一个变量引用的时候,就创建了一个闭包。

闭包的特性:

  ①.封闭性:外界无法访问闭包内部的数据,如果在闭包内声明变量,外界是无法访问的,除非闭包主动向外 界提供访问接口;

  ②.持久性:一般的函数,调用完毕之后,系统自动注销函数,而对于闭包来说,在外部函数被调 用之后,闭包结构依然保存在;

对页面的影响:

  使用闭包会占有内存资源,过多的使用闭包会导致内存溢出等。

function f1() {
  var n = 999;
  function f2() {
		console.log(n);
	}
  return f2;
}
var result = f1();
result(); // 999

闭包就是函数f2,即能够读取其他函数内部变量的函数

闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。

应用:

  1. 局部变量 : 在函数中定义有共享意义的局部变量。注: 定义成全局变量会对外部造成污染

  2. 内嵌函数: 在函数声明中有内嵌函数, 内嵌函数对函数中的局部变量进行访问。

  3. 外部使用: 函数向外返回此内嵌函数, 外部可以通过此内嵌函数持有并访问声明在函数内部的局部变量, 而此变量外部是通过其他途径无法访问的。

原生js操作dom的方法

方法描述
getElementById()返回带有指定 ID 的元素。
getElementsByTagName()返回包含带有指定标签名称的所有元素的节点列表(集合/节点数组)。
getElementsByClassName()返回包含带有指定类名的所有元素的节点列表。
appendChild()把新的子节点添加到指定节点。
removeChild()删除子节点。
replaceChild()替换子节点。
insertBefore()在指定的子节点前面插入新的子节点。
createAttribute()创建属性节点。
createElement()创建元素节点。
createTextNode()创建文本节点。
getAttribute()返回指定的属性值。
setAttribute()把指定属性设置或修改为指定的值。

浏览器缓存有几种及其区别

缓存: localStore 、cookie 、session

localStore: 5M 不可跨域 需要手动删除,否则永久保存 cookie: 4K 可跨域 数据保存于本地浏览器(客户端) 不安全 session: 数据保存于服务器端 时效端 (访问增多时,会占用服务器的资源) cookie和session的共同之处: 均是用来跟踪浏览器用户身份的会话方式。

26、JavaScript原型,原型链?

万物皆对象,原型链查找就是找的所属构造函数的原型,层级查找,层级继承 , 顶层是null

![image-20210301171512770](/Users/jing/Library/Application Support/typora-user-images/image-20210301171512770.png)

原型:定义所有实例对象共享的属性和方法它是函数的一个属性

JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型……

原型链:是一种关系,实例对象和原型对象之间的关系,关系是通过原型(proto)来联系的

大神的解释:js的对象有一个内置属性指向自己的原型

####JavaScript原型链 ? 有什么特点?(重要)

  1. JS中每个函数都存在有一个原型对象属性prototype。并且所有函数的默认原型都是Object的实例。
  2. 每个继承父函数的子函数的对象都包含一个内部属性_proto_。该属性包含一个指针,指向父函数的prototype。若父函数的原型对象的_proto_属性为再上一层函数。在此过程中就形成了原型链。
  3. 原型链实现了继承。原型链存在两个问题:a 包含引用类型值的原型属性会被所有实例共享。b 在创建子类型时,无法向超类型的构造函数中传递参数。

JS实现数组去重方法

一、利用 Set 去重

/* 
  1.返回值是一个去重的数组 
  2.注意 Number 和 String 类型
*/
var arr = ['one','two','three','one','three','two','four'];
let el = new Set(arr);
console.log(el); // ['one','two','three','four'];

二、利用 indexOf() 和 lastIndexOf() 去重

/*
  indexOf:从左往右查找目标字符串,是否包含 Value;
           如果包含,返回第一次出现的索引;
           如果不包含,返回 -1
  indexOf 和 lastIndexOf() 方法一样
  步骤:
  1. 先声明一个空数组,用来存放去重后的数据
  2. 遍历数组,判断每一项
*/
let arr = ['one','two','three','one','three','two','four'];
let indexArr = [];
arr.forEach(item => {
   if(indexArr.indexOf(item)===-1){
      indexArr.push(item);
   };
});
console.log(indexArr); // ['one','two','three','four'];

三、利用 filter去重

let arr = ['one','two','three','one','three','two','four'];
let el = arr.filter((item,index)=>arr.indexOf(item)===index);
console.log(el); // ['one','two','three','four'];

四、利用对象特性去重

(1)对象方法 和 for 循环

/*
  1.声明一个对象 obj,利用对象特性
  2.循环每一项复印,使用 keys(values) 方法取出 key 值
*/
var arr = ['one','two','three','one','three','two','four'];
var obj = {};
for(var i=0;i<arr.length;i++){
    obj[arr[i]] = arr[i];
};
var el =  Object.keys(obj);
console.log(el) // ['one','two','three','four'];

####null、undefined和未声明变量之间有什么区别?如何检查判断这些状态值?

已声明但是没有初始化的变量的值为undefined. 而未声明的变量虽然用 typeof 操作符结果为 undefined, 但是并没有值.

null 是一个空的对象引用. undefined 是声明但没有被赋值的变量. 利用这两个就可以区分空对象指针和未经初始化的变量.

undefined 值是派生自 null 值的. 所以对于它们的相等性测试, 返回 true

alert(undefined == null); // 返回 true

####请描述事件冒泡

事件冒泡: 当一个元素上的事件被触发的时候,比如说鼠标点击了一个按钮,同样的事件将会在那个元素的所有祖先元素中被触发。这一过程被称为事件冒泡;这个事件从原始元素开始一直冒泡到DOM树的最上层。

####JavaScript中的作用域与变量声明提升?

Js(cs6之前)中只有两种作用域全局作用域和函数作用域。全局作用域的变量和函数可以在任意位置调用。函数作用域内的变量和函数只能在函数内部使用。

函数作用域内的变量提升,局部变量在整个函数体始终是有定义的,我们可以将变量声明”提前“到函数体顶部,同时变量初始化还在原来位置。

var scope="global";  
function t(){  
  console.log(scope);  //undefined
	var scope="local"  
	console.log(scope);  
}  
t(); 

全局作用域内的变量提升,

使用var关键字声明的全局变量也会将变量的声明提升到头部

console.log(scope);//undefined
var scope="global";  

target和currentTarget的区别

  • target是事件触发的真实元素

  • currentTarget是事件绑定的元素

  • 事件处理函数中的this指向是中为currentTarget

  • currentTarget和target,有时候是同一个元素,有时候不是同一个元素 (因为事件冒泡)

    • 当事件是子元素触发时,currentTarget为绑定事件的元素,target为子元素
    • 当事件是元素自身触发时,currentTarget和target为同一个元素。

target:触发事件的源组件(事件注册/绑定所在组件)

currentTarget:事件触发的当前事件(当前事件,可能是触发事件的源组件,可能是触发的事件组件(即触发事件源组件的子元素),此时点击子元还是父元素,都是当前事件,应用e.currentTarget)。

JS中的宏任务和微任务的区别和用法

宏任务一般是:包括整体代码scriptsetTimeoutsetIntervalI/OUI render。 微任务主要是:PromiseObject.observeMutationObserver

vue.js

####vue实例的生命周期

主要的生命周期函数分类:

创建期间的生命周期函数:

beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性

created:实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板

beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中

mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示

运行期间的生命周期函数:

beforeUpdate:状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点

updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!

销毁期间的生命周期函数:

beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。

destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

![image-20190930090231773](file:///Users/jing/Library/Application Support/typora-user-images/image-20190930090231773.png?lastModify=1614516486)

####Vue渲染过程浅析

Vue 推荐在绝大多数情况下使用 template 来创建你的 HTML。但是模板毕竟是模板,不是真实的dom节点。从模板到真实dom节点还需要经过一些步骤

  1. 把模板编译为render函数
  2. 实例进行挂载, 根据根节点render函数的调用,递归的生成虚拟dom
  3. 对比虚拟dom,渲染到真实dom
  4. 组件内部data发生变化,组件和子组件引用data作为props重新调用render函数,生成虚拟dom, 返回到步骤3

使用vue输出 hello world

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
</head>
<script src="https://unpkg.com/vue@2.6.10/dist/vue.js"></script>
<body>
    <div id="app">
        <p>{{ msg }}</p>
    </div>
    <!-- 在这里直接使用msg就可以渲染页面 -->

    <script>
        //创建一个vue的实例
        var vm = new Vue({
            el: '#app', 
            data: {
                msg: 'Hello world!!!'
            }
        })
    </script>
</body>
</html>

vue组件中data为什么必须是函数

首先,如果不是一个函数,Vue直接就会报错。 其次,原因是在于Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。

vue中watch和computed 的区别

  1. computed属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算,要当作属性来使用。computed可以监听v-model(data)中的值,只要值发生变化 他就会重新去计算。computed必须是要有一个返回值;
  2. methods方法表示一个具体的操作,主要书写业务逻辑;
  3. watch一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作。watch除了可以监听data中值的变化,还可以监听路由的变化。watch中有两个参数 分别是新值和旧值。可以看作是computedmethods的结合体;

vue中常用的修饰符有哪些

v-model修饰符
lazy修饰符:
默认情况下,v-model默认是在input事件中同步输入框的数据的。
也就是说,一旦有数据发生改变对应的data中的数据就会自动发生改变。
lazy修饰符可以让数据在失去焦点或者回车时才会更新:
number修饰符:
默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理。
但是如果我们希望处理的是数字类型,那么最好直接将内容当做数字处理。
number修饰符可以让在输入框中输入的内容自动转成数字类型:
trim修饰符:
如果输入的内容首尾有很多空格,通常我们希望将其去除
trim修饰符可以过滤内容左右两边的空格

v-on修饰符
.stop - 调用 event.stopPropagation()。
.prevent - 调用 event.preventDefault()。
.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
.native - 监听组件根元素的原生事件。
.once - 只触发一次回调。

![image-20210228172050050](/Users/jing/Library/Application Support/typora-user-images/image-20210228172050050.png)

Vue组件间通信方式都有哪些?

整理vue8种常规的通信方案

  1. 通过 props 传递

  2. 通过 $emit 触发自定义事件

  3. 使用 ref

  4. EventBus

  5. $parent 或 $root

  6. Vuex

  7. attrs 与 listeners

  8. Provide 与 Inject

v-model原理

v-model其实是一个语法糖,它的背后本质上是包含两个操作: 1.v-bind绑定一个value属性 2.v-on指令给当前元素绑定input事件

v-router路由

$route$router是有区别的 $router为VueRouter实例,想要导航到不同URL,则使用$router.push方法 $route为当前router跳转对象里面可以获取name、path、query、params等

什么是导航守卫(全局守卫)? vue-router提供的导航守卫主要用来监听监听路由的进入和离开的. vue-router提供了beforeEach和afterEach的钩子函数, 它们会在路由即将改变前和改变后触发.

导航钩子的三个参数解析: to: 即将要进入的目标的路由对象. from: 当前导航即将要离开的路由对象. next: 调用该方法后, 才能进入下一个钩子

如果是后置钩子, 也就是afterEach, 不需要主动调用next()函数

路由独享的守卫

路由配置上直接定义 beforeEnter

组件内的守卫

路由组件内直接定义以下路由导航守卫:

  • beforeRouteEnter

  • beforeRouteUpdate (2.2 新增)

  • beforeRouteLeave

####keep-alive

keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。 它们有两个非常重要的属性: include - 字符串或正则表达,只有匹配的组件会被缓存 exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存

Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。(以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面。然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用)

ES6

forEach、map、 some 、 filter 、 findIndex

这些都属于数组的新方法,都会对数组中的每一项,进行遍历,执行相关的操作;

forEach()

forEach没有返回值。

是一个普通的for循环,写法上比for循环简单,适用于普通的循环。

接收三个参数:每个数组的元素,下标index,自身。

let a1 = ['hello', 'world']
  let b1 = a1.forEach((item, i, arr) => {
      console.log(item, i, arr)
  })
console.log(b1) // undefined

map()

map返回一个新数组。

在使用过程中当需要返回一个新的数组时,建议优先使用map,而不是forEach

let a2 = [1, 2, 3]
let b2 = a2.map(v => {
    return v * 2
})
console.log(a2) // [1, 2, 3]
console.log(b2) // [2, 4, 6]

filter()

filter返回一个新数组。

返回符合指定要求的数组

let a3 = [
        {
            title: '标题1',
            show: true
        },
        {
            title:'标题2',
            show: false
        },
        {
            title:'标题3',
            show: true
        }
    ]
    let b3 = a3.filter(item => {
        return item.show // 返回show为true的数据
    })
    console.log(b3) // [{title: "标题1", show: true},{title: "标题3", show: true}]

some()

some返回一个Boolean类型数据。

当数组中其中一个元素满足指定要求即返回true,否则返回false

let a4 = ['yellow', 'red', 'blue']
let b4 = a4.some(v => {
  return v === 'red'
})
console.log(b4) // true

every()

every返回一个Boolean类型的数据。

当数组中所有的元素都满足指定要求即返回true,否则返回false

let a5 = [2, 4, 6, 8]
let b5 = a5.every(v => {
  return v % 2 === 0 // 判断数组中的元素是不是都是偶数
})
console.log(b5) // true

reduce()

reduce可用于计算数组的和

let a6 = [1, 2, 3, 4, 5, 6 ,7, 8, 9, 10]
/**
 * preTotal:之前值的和
 * currentValue: 当前值
 */
let b6 = a6.reduce((preTotal, currentValue) => {
  console.log(currentValue) /*1, 2 ... 7, 8, 9*/
  return preTotal + currentValue
})
console.log(b6) // 55

find()

find返回通过指定条件(函数内判断)的数组的第一个元素的值。

let a8 = [
  {
    name: 'Tom',
    age: 18
  },
  {
    name: 'Anny',
    age: 15
  },
  {
    name: 'Kobe',
    age: 22
  },
]
let b8 = a8.find((item) => {
  return item.age > 15
})
console.log(b8) // {name: 'Tom', age: 18}

let/var

1、事实上var的设计可以看成JavaScript语言设计上的错误. 但是这种错误多半不能修复和移除, 以为需要向后兼容. 2、大概十年前, Brendan Eich就决定修复这个问题, 于是他添加了一个新的关键字: let. 3、我们可以将let看成更完美的var

块级作用域 JS中使用var来声明一个变量时, 变量的作用域主要是和函数的定义有关. 针对于其他块定义来说是没有作用域的,比如if/for等,这在我们开发中往往会引起一些问题。

使用let、var和const创建变量有什么区别?

// let 是块级作用域,函数内部使用let 定义后,对函数外部无影响,如果不初始化输出的话,会报语法错误
let c = 3;
console.log('函数外let定义c:' + c);//输出c=3
function change(){
	let c = 6;
	console.log('函数内let定义c:' + c);//输出c=6
} 
change();
console.log('函数调用后let定义c不受函数内部定义影响:' + c);//输出c=3

// const 是全局作用域,const 声明的变量,不可以直接修改,必须初始化,const 一般用在数组的定义和修改中
 
	const b = 2;//正确
	// const b;//错误,必须初始化 
	console.log('函数外const定义b:' + b);//有输出值
	// b = 5;
	// console.log('函数外修改const定义b:' + b);//无法输出
 
// var 全局变量,声明的变量可以修改,如果不初始化输出的话,会报undefined,但不会报错
 
var a = 1;
// var a;//不会报错
console.log('函数外var定义a:' + a);//可以输出a=1
function change(){
	a = 4;
	console.log('函数内var定义a:' + a);//可以输出a=4
} 
change();
console.log('函数调用后var定义a为函数内部修改值:' + a);//可以输出a=4

const的使用

const关键字 在很多语言中已经存在, 比如C/C++中, 主要的作用是将某个变量修饰为常量. 在JavaScript中也是如此, 使用const修饰的标识符为常量, 不可以再次赋值. 什么时候使用const呢? 当我们修饰的标识符不会被再次赋值时, 就可以使用const来保证数据的安全性. 建议: 在ES6开发中,优先使用const, 只有需要改变某一个标识符的时候才使用let.

 // 1.注意一: 一旦给const修饰的标识符被赋值之后, 不能修改
  // const name1 = 'why';
  // name1 = 'abc';

  // 2.注意二: 在使用const定义标识符,必须进行赋值
  // const name;

  // 3.注意三: 常量的含义是指向的对象不能修改, 但是可以改变对象内部的属性.
  const obj = {
    name: 'why',
    age: 18,
    height: 1.88
  }
  // obj = {}
  console.log(obj);

  obj.name = 'kobe';
  obj.age = 40;
  obj.height = 1.87;

  console.log(obj);

4、对象字面量增强写法

// 1.属性的增强写法
  const name = 'why';
  const age = 18;
  const height = 1.88
  // ES5的写法
  const obj11 = {
    name: name,
    age: age,
    height: height
  }
  const obj = {
    name,
    age,
    height,
  }
  console.log(obj);
	
	// 2.函数的增强写法
  // ES5的写法
  const obj = {
    run: function () {

    },
    eat: function () {

    }
  }
  const obj = {
    run() {

    },
    eat() {

    }
  }