JS面试题

延迟加载JS有哪些方式?

async defer 都可以做到延迟加载js
例如:<script defer type="text/javascript" src="script.js">
defer:等html全部解析完成,才会执行js代码,顺次执行js脚本
async:async是和html解析同步的(一起的),不是顺次执行js脚本(谁先加载完谁先执行)

面试题:JS数据类型有哪些?

基本类型
string number boolean null undefined symbol bigint
引用类型
object

面试题: JS数据类型

console.log(true+1) //2
console.log('name'+true) //'nametrue'
字符串和其他类型 相加 变成链接类型
console.log(undefined +1) //NaN
console.log(typeof null) //object


image.png
typeof(NaN)
'number'
typeof(undefined)
'undefined'
typeof(null)
'object'
NaN 是一个数值类型,但是不是一个具体的数字。

面试题:null和undefined的区别?

1、作者在设计js的都是先设计null(为什么设计null:最初设计js的时候借鉴了Java的语言)
2、null会被隐式转换成0,很不容易发现错误。
3、先有null后有undefined,出来undefined是为了填补之前的坑。
具体区别:
javascript的最初版本是这样区分的,null是一个表示“无”的对象(空对象指针),转为数值时为0,undefined是一个表示“无”的原始值,转为数值时为NaN.

面试题:==和===有什么不同?

>==:比较的是值。
 ===:除了比较值,还比较类型。

面试题:JS微任务和宏任务(知不知道js是怎么执行的以及执行顺序)

1、js是单线程语言
2、js代码执行流程:同步执行完=>事件循环
同步的任务都执行完了,才会执行事件循环的内容
进入事件循环 :请求 、定时器、事件...
3、事件循环中包含:【微任务 、宏任务】
微任务:promise.then
宏任务:setTimeout
要执行宏任务的前提是清空了所有的微任务
流程:同步=》事件循环【微任务和宏任务】=》微任务=》宏任务=》微任务....
setTimeout(function(){
console.log(1)
})
new Promise((resolve)=>{
console.log('Promise 1') //同步
resolve()
}).then(()=>{
console.log('微1')
}).then(()=>{
console.log('微2')
})
console.log(2) //同步
//结果:Promise 1,2,1,微2,1

面试题:JS作用域考题

1、除了函数外,js是没有块级作用域的。
2、作用域链:内部可以访问外部的变量,但是外部不能访问内部的变量。
注意:如果内部有,优先查找到内部,如果内部没有就查找外部的。
3、注意声明变量是用var还是没有写(window.)
4、注意:js有变量提升的机制【变量悬挂声明】

  1. 优先级:声明变量 > 声明普通函数 > 参数 > 变量提升
面试的时候怎么看:
  1. 本层作用域有没有此变量【注意变量提升】
  2. 注意:js除了函数外没有块级作用域
  3. 普通声明函数是不看写函数的时候顺序

考题一:

function c(){
    var b = 1;
    function a(){
        console.log( b ); // undefined
        var b = 2;
        console.log( b );//2
    }
    a();
    console.log( b ); //1
}
c();

考题二:

var name = 'a';
(function(){ //var name ;
    if( typeof name == 'undefined' ){
        var name = 'b';
        console.log('111'+name); //111b
    }else{
        console.log('222'+name);
    }
})()

考题三:

function fun( a ){
    var a = 10;
    function a(){}
    console.log( a );//100
}
fun( 100 );
function fun( a ){
    var a = 10;
    function a(){}
    console.log( a );//10
}
fun(  );

js对象考题

js对象注意点 new创建新对象

  1. 对象是通过new操作符构建出来的,所以对象之间不相等(除了引用外);
  2. 对象注意:引用类型(共同一个地址);
  3. 对象的key都是字符串类型;
    4.对象如何找属性|方法
    查找规则:先在对象本身找==》函数 (构造函数中找)==》对象原型中找 ==》构造函数原型中找==》对象上一层原型查找

考题一:

 [1,2,3] === [1,2,3]   //false

考题二:

 var obj1 = {
    a:'hellow'
}
var obj2 = obj1; //引用地址相同
obj2.a = 'world';
console.log(obj1);  // {a:'world'}
(function(){
    console.log(a); //变量提升 undefined    
    var a = 1;
})();

考题三:

var a = {}
var b = {
    key:'a'
}
var c = {
    key:'c'
}

a[b] = '123';
a[c] = '456';

console.log( a[b] ); //456

JS作用域+this指向+原型的考题

考题一:

function Foo(){
    getName = function(){console.log(1)} //注意是全局的window.
    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
getName();    //4 声明变量>声明普通函数

//getName = function(){console.log(1)} //注意是全局的window.
Foo().getName();  //1
getName();       //1
new Foo().getName(); //3
//先在对象本身找 ===> 构造函数中找 ===> 对象原型中找 ===> 构造函数原型中找 ===> 对象上一层原型查找

考题二:

var o = {
    a:10,
    b:{
        a:2,
        fn:function(){
            console.log( this.a ); //2
            console.log( this );  //代表b对象
        }
    }
}
o.b.fn();

考题三:

window.name = 'ByteDance';
function A(){
    this.name = 123;
}
A.prototype.getA = function(){
    console.log( this );
    return this.name + 1;
}
let a = new A();
let funcA = a.getA;
funcA(); // this 代表window  ByteDance1

考题四:

var length = 10;
function fn(){
    return this.length + 1;
}
var obj = {
    length:5,
    test1:function(){
        return fn();
    }
}
obj.test2 = fn;
console.log( obj.test1() ); //11
console.log( fn()===obj.test2() );//11,6 //false
console.log( obj.test1() == obj.test2() ); //11 6 //false


JS判断变量是不是数组,你能写出哪些方法?

方式一:isArray

var arr = [1,2,3]
console.log(Array.isArray(arr))

方式二:instanceof [可写 可不写]

var arr = [1,2,3]
console.log(arr instanceof Array )

方式三:原型prototype

var arr = [1,2,3]
console.log(Object.prototype.toString.call(arr).indexOf('Array')>-1 )

方式四:isPrototypeOf()

var arr = [1,2,3]
console.log(Array.prototype.isPrototypeOf(arr))

方式四:constructor

var arr = [1,2,3]
console.log(  arr.constructor.toString().indexOf('Array') > -1 )

slice是干嘛的、splice是否会改变原数组

  1. slice是来截取的
    参数可以写slice(3)、slice(1,3)、slice(-3)
    返回的是一个新的数组
  2. splice 功能有:插入、删除、替换
    返回:删除的元素
    该方法会改变原数组

JS数组去重

方式一:new set

var arr1 = [1,2,3,4,5,6,6,6]
function unique(arr1){
  return [...new Set(arr1)]
}
console.log(unique(arr1))

方式二:indexOf

var arr1 = [1,2,3,4,5,6,6,6]
function unique(arr){
   var brr = []
   for(var i = 0 ; i<arr.length;i++){
        if(brr.indexOf(arr[i]) ==  -1){
              brr.push(arr[i])
        }
    }
     return brr
}
console.log(unique(arr1))

方式三:sort

var arr1 = [1,2,3,4,5,6,6,6]
function unique(arr){
   arr = arr.sort()
   var brr = []
   for(var i = 0 ; i<arr.length;i++){
        if(arr[i] !== arr[i-1]){
              brr.push(arr[i])
        }
    }
     return brr
}
console.log(unique(arr1))

找出多维数组最大值

function fnArr(arr){
  var newArr = [];
  arr.forEach((item,index)=>{
      newArr.push(Math.max(...item))
   })
  return newArr;
}
console.log(fnArr([
    [4,5,1,3],
    [13,27,18,26],
    [32,35,37,39],
    [1000,1001,857,1]
]));

面试题:给字符串新增方法实现功能

给字符串对象定义一个addPrefix函数,当传入一个字符串str时,它会返回新的带有指定前缀的字符串,例如:console.log( 'world'.addPrefix('hello') ) 控制台会输出helloworld
String.prototype.addPrefix = function(str){
  return str+this
}
console.log('world'.addPrefix('hello'))

面试题:面试题:找出字符串出现最多次数的字符以及次数

var str = 'aaabbbbbccddddddddddx';
var obj = {};
for(var i=0;i<str.length;i++){
    var char = str.charAt(i);
    if( obj[char] ){
        obj[char]++;
    }else{
        obj[char] = 1;
    }
}
console.log( obj );
//统计出来最大值
var max = 0;
for( var key in obj ){
    if( max < obj[key] ){
        max = obj[key];
    }
}
//拿最大值去对比
for( var key in obj ){
    if( obj[key] == max ){
        console.log('最多的字符是'+key);
        console.log('出现的次数是'+max);
    }
}

new操作符具体做了什么

  1. 创建了一个空的对象
  2. 将空对象的原型,指向于构造函数的原型
  3. 将空对象作为构造函数的上下文(改变this指向)
  4. 对构造函数有返回值的处理判断
function Fun( age,name ){
    this.age = age;
    this.name = name;
}
function create( fn , ...args ){
    //1. 创建了一个空的对象
    var obj = {}; //var obj = Object.create({})
    //2. 将空对象的原型,指向于构造函数的原型
    Object.setPrototypeOf(obj,fn.prototype);
    //3. 将空对象作为构造函数的上下文(改变this指向)
    var result = fn.apply(obj,args);
    //4. 对构造函数有返回值的处理判断
    return result instanceof Object ? result : obj;
}
console.log( create(Fun,18,'张三')   )

面试题:闭包

1. 闭包是什么
闭包是一个函数加上到创建函数的作用域的连接,闭包“关闭”了函数的自由变量。
2. 闭包可以解决什么问题【闭包的优点】
   2.1内部函数可以访问到外部函数的局部变量
   2.2 闭包可以解决的问题
     var lis = document.getElementsByTagName('li');
      for(var i=0;i<lis.length;i++){
        (function(i){
          lis[i].onclick = function(){
            alert(i);
          }
        })(i)
      }
3. 闭包的缺点
  3.1 变量会驻留在内存中,造成内存损耗问题。
                解决:把闭包的函数设置为null
  3.2 内存泄漏【ie】 ==> 可说可不说,如果说一定要提到ie

面试题:原型链

1、原型可以解决什么问题?
对象共享属性和共享方法
2、谁有原型
函数拥有 prototype
对象拥有 proto
3、对象查找属性或者方法的顺序
先在对象本身查找==》构造函数中查找==》对象原型中找==》构造函数中的原型中找==》当前的原型的原型中找
4、原型链
4.1是什么?就是把原型串联起来
4.2 原型链的最顶端是null

面试题: JS继承有哪些方式

方式一:ES6

class Parent{
    constructor(){
        this.age = 18;
    }
}

class Child extends Parent{
    constructor(){
        super();
        this.name = '张三';
    }
}
let o1 = new Child();
console.log( o1,o1.name,o1.age );

方式二:原型链继承

function Parent(){
    this.age = 20;
}
function Child(){
    this.name = '张三'
}
Child.prototype = new Parent();
let o2 = new Child();
console.log( o2,o2.name,o2.age );

方式三:借用构造函数继承

function Parent(){
    this.age = 22;
}
function Child(){
    this.name = '张三'
    Parent.call(this);
}
let o3 = new Child();
console.log( o3,o3.name,o3.age );

方式四:组合式继承

function Parent(){
    this.age = 100;
}
function Child(){
    Parent.call(this);
    this.name = '张三'
}
Child.prototype = new Parent();
let o4 = new Child();
console.log( o4,o4.name,o4.age );

面试题:说一下call、apply、bind区别

共同点:功能一致

可以改变this指向
语法:函数.call() 、函数.apply()、函数.bind()

var str = '你好'
var obj = { str :'stsaa'}
function fun(){
  console.log(this,this.str)
}
fun.call(obj) //call立即执行
fun.apply(obj) //apply立即执行
fun.bind(obj)() //bind不会立即执行 因为bind返回的是函数
fun()

var str = '你好'
var obj = { str :'stsaa'}
function fun(name,age){
  this.name = name
  this.age = age
  console.log(this,this.str)
}
// fun.call(obj) //call立即执行
// fun.apply(obj) //apply立即执行
// fun.bind(obj)() //bind不会立即执行 因为bind返回的是函数
// fun()
fun.call(obj,'张三',88) //参数需要挨个写
fun.apply(obj,['张三',88])  //参数是数组
fun.bind(obj,'张三',88)() //参数需要挨个写
// fun()

区别:

1、call、apply可以立即执行。bind不会立即执行,因为返回的是一个函数需要加入()执行。
2、参数不同:apply第二个参数是数组。call和bind有多个参数挨个写。

场景:

1、call、apply可以立即执行。bind不会立即执行,因为返回的是一个函数需要加入()执行。
2、参数不同:apply第二个参数是数组。call和bind有多个参数挨个写。

场景:
1. 用apply的情况
var arr1 = [1,2,4,5,7,3,321];
console.log( Math.max.apply(null,arr1) )

2. 用bind的情况
var btn = document.getElementById('btn');
var h1s = document.getElementById('h1s');
btn.onclick = function(){
    console.log( this.id );
}.bind(h1s)

面试题:深拷贝和浅拷贝

共同点:复制
1、浅拷贝:只复制引用,而未复制真正的值。//相互受影响
var arr1 = ['a','b','c','d']
var arr2 = arr1;
arr1[0] = '你好吗'
arr2[1] = '还行'
console.log(arr1,arr2)

var obj1 = {a:1,b:2}
var obj2 = Object.assign(obj1);


2. 深拷贝:是复制真正的值 (不同引用)
var obj3 = {
    a:1,
    b:2
}
var obj4 = JSON.parse(JSON.stringify( obj3 ));

//递归的形式
function copyObj( obj ){
    if(  Array.isArray(obj)  ){
        var newObj = [];
    }else{
        var newObj = {};
    }
    for( var key in obj ){
        if( typeof obj[key] == 'object' ){
            newObj[key] = copyObj(obj[key]);
        }else{
            newObj[key] = obj[key];
        }
    }
    return newObj;
}
console.log(  copyObj(obj5)  );

面试题:localStorage、sessionStorage、cookie的区别

共同点:在客户端存放数据
区别:
1、数据存放有效期
sessionStorage : 仅在当前浏览器窗口关闭之前有效。【关闭浏览器就没有了】
localStorage : 始终有效,窗口或者浏览器关闭也一直保存,所以叫持久化存储。
cookie:只在设置的cookie过期时间之前有效,即使窗口或者浏览器关闭也有效。
2、localStorage、sessionStorage不可以设置过期时间
cookie 有国企时间、可以设置过期(把时间调整到之前的时间,就过期了)
3、存储大小的限制
cookie 存储量不能超过4k
localStorage 、sessionStorage 不能超过5M
****根据不同的浏览器存储的大小不同的***

面试题:什么是语义化标签

  1. 易读性和维护性更好。
  2. seo成分会更好,蜘蛛抓取更好。
  3. IE8不兼容HTML5标签的。解决:可以通过html5shiv.js去处理。

面试题:::before 和 :after中双冒号和单冒号 有什么区别?解释一下这2个伪元素的作用

  1. 区别
    :是伪类、::伪元素 ===》是为了做区分
    2.是什么?作用
    元素before之前 、 元素after之后
    作用:清除浮动、样式布局上也有作用

面试题:如何关闭IOS键盘首字母自动大写

<input type="text" autocapitalize='off'>

面试题:怎么让Chrome支持小于12px 的文字?

Chrome默认字体大小是:16px
**每个浏览器默认字体大小可能都不一样

<style type="text/css">
div{
    font-size:10px;
}
div span{
    display: inline-block;
    -webkit-transform:scale(1.6);
}
</style>

面试题:rem和em区别

相对于font-size

em针对于父元素的font-size
rem针对于根(html)元素的font-size

面试题:ios系统中元素被触摸时产生的半透明灰色遮罩怎么去掉

<style>
    a,button,input,textarea{
        -webkit-tap-highlight-color: rgba(0,0,0,0);
    }
</style>

面试题:webkit表单输入框placeholder的颜色值能改变吗?

<style type="text/css">
    input::-webkit-input-placeholder{
        color:red;
    }
</style>

面试题:禁止ios长按时触发系统的菜单,禁止ios&android长按时下载图片

禁止ios 长按时触发系统的菜单,禁止ios&android长按时下载图片
html,body{
    touch-callout: none;
    -webkit-touch-callout: none;
    
    user-select:none;
    -webkit-user-select:none;
}

面试题:禁止ios和android用户选中文字

html,body{
    user-select:none;
    -webkit-user-select:none;
}

面试题:自适应

淘宝无限适配【移动端】:淘宝无限适配 + 布局单位使用rem

响应式

1. 是什么?
    一个URL可以响应多端
2. 语法结构
    @media only screen and (max-width: 1000px){
        ul li:last-child{
            display: none;
        }
    }

    only : 可以排除不支持媒体查询的浏览器
    screen : 设备类型
    max-width | max-height
    min-width | min-height 
3. 响应式图片【性能优化】
    <picture>
        <source srcset="1.jpg" media='(min-width:1000px)'>
        <source srcset="2.jpg" media='(min-width:700px)'>
        <img srcset="3.jpg">
    </picture>

面试题:响应式

1. 是什么?
    一个URL可以响应多端
2. 语法结构
    @media only screen and (max-width: 1000px){
        ul li:last-child{
            display: none;
        }
    }

    only : 可以排除不支持媒体查询的浏览器
    screen : 设备类型
    max-width | max-height
    min-width | min-height 
3. 响应式图片【性能优化】
    <picture>
        <source srcset="1.jpg" media='(min-width:1000px)'>
        <source srcset="2.jpg" media='(min-width:700px)'>
        <img srcset="3.jpg">
    </picture>

布局方案

一、什么情况下采用响应式布局
    
    数据不是特别多,用户量不是特别大,纯展示类的项目适合响应式布局
    
    例如:公司的官网、专题页面
    
    特别追求性能的项目,不太适合响应式,因为如果添加了很多的响应式就会造成加载速度变慢。

二、pc + 移动端应该做什么样的布局方案
    注意:访问量还可以或者比较大,类似于淘宝网。
    
    pc是一套,会加入一点点响应式。
    移动端是一套,会使用自适应的布局方式。

三、pc的设计图

    ui:1980
    笔记本电脑:1280
    ui图的宽度和电脑的宽度不对应该怎么办?
        1. 把ui图进行等比缩放,缩放成和电脑一样的尺寸
        2. 换1980的电脑
        
四、移动端的设计图

    宽度:750
    因为750设计图/2就是375,正好是iphone6的尺寸,我们要把iphone6的尺寸做为基准点。

面试题:var、let、const区别

var、let、const 共同点都是可以声明变量的
区别一:
var 具有变量提升的机制
let和const没有变量提升的机制
区别二:
var 可以多次声明同一个变量
let和const不可以多次声明同一个变量
区别三:
var、let声明变量的
const声明常量
var和let声明的变量可以再次赋值,但是const不可以再次赋值了。
区别四:
var声明的变量没有自身作用域
let和const声明的变量有自身的作用域

面试题:作用域考题

考题一:let和const没有变量提升性

console.log( str );//undefined
var str = '你好';

console.log( num );//报错
let num = 10;
    var n = 2;
    if( true ){
        var n = 1;
    }
    console.log( n );//1
}
demo();


function demo(){
    let n = 2;
    if( true ){
        let n = 1;
    }
    console.log( n );//2
}
demo();

考题三:可以修改

const obj = {
    a:1
}
obj.a = 11111;
console.log( obj ) //{a:11111}

const arr = ['a','b','c'];
arr[0]= 'aaaaa';
console.log( arr ); //['aaaaa','b','c']

面试题:将下列对象进行合并

方式一:Object.assign

const a = {a:1,b:4};
const b = {b:2,c:3};

let obj1 = Object.assign(a,b);
console.log( obj1 );

方式二:...

let obj2 = {...a,...b};
console.log( obj2 );

方式三:自己封装方法

function extend( target,  source ){
    for(var key in source){
        target[key] = source[key];
    }
    return target;
}
console.log( extend(a,b) );

面试题:箭头函数和普通函数有什么区别?

  1. this指向的问题
    箭头函数中的this只在箭头函数定义时就决定的,而且不可修改的(call、apply、bind)
    ****箭头函数的this指向定义时候、外层第一个普通函数的this
  2. 箭头函数不能new(不能当作构造函数)
  3. 箭头函数prototype
  4. 箭头函数arguments

面试题:Promise有几种状态

有三种状态:
pending(进行中)
fulfilled(已成功)
rejected(已失败)

面试题:find和filter的区别

区别一:返回的内容不同
filter 返回是新数组
find 返回具体的内容
区别二:
find :匹配到第一个即返回
filter : 返回整体(没一个匹配到的都返回)

面试题:some和every的区别

some ==》 如果有一项匹配则返回true
every ==》 全部匹配才会返回true

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,738评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,377评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,774评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,032评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,015评论 5 361
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,239评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,724评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,374评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,508评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,410评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,457评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,132评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,733评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,804评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,022评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,515评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,116评论 2 341

推荐阅读更多精彩内容