前端页面优化之完善篇

前端页面优化

学习目标

  • 了解为何进行前端页面的优化

  • 了解SEO,掌握前端页面语义性的评判标准与规则

  • 从代码可读性角度进行页面优化

为何进行前端优化

  1. 加载速度更快 —— 速度就是王道
  2. 成本耕地 —— 人力与时间成本
    • 网站制作完成 ≠ 开发结束
  3. 扩展性更强 —— 前端工作的非独立性

前端页面的优化角度

代码语义性(与SEO相关)

  1. 语义性的重要性
    1. 语义性对SEO以及网站自然排名的影响
    2. SEO是什么
    3. 对语义性的要求使得网站发生了什么变化
      1. table布局 -> DIV + CSS(HTML + CSS)
      2. Flash类网站的消亡
  2. 搜索引擎爬虫的评判因素
    1. 良好的缩进与规范的格式
    2. 良好的扩展性(代码量小)
    3. 合理的标签语义
    4. 合理的标签的嵌套
    5. 404页面必不可少
  3. 标签选择
    1. title与meta
    2. 标签语义化
      1. div、span
      2. h1-h6、p
      3. ul、ol、li、dl、dt、dd
      4. a、img、table
    3. 特殊属性
      1. img添加alt和title属性
      2. a添加title的属性

代码可读性

  1. 标签的嵌套规则
    1. ins和del(行内元素)可以包含块级元素或者行内元素,其他任何行内元素都不允许包含块级元素
    2. p、h1-h6可以直接包含行内元素的文本信息,但是不允许包含块级元素
    3. dl元素只允许包含dt和dd,同时dt不能包含块状元素,只允许包含行内元素,dd可以包含任何元素
    4. 不建议from元素直接包含input元素
    5. table元素建议直接包含caption、colgroup、col、thead、tbody、tfoot,不建议直接包含tr货主其他任何元素
  2. CSS代码书写规范
    1. 命名采用更简明有语义的英文单词进行组合
    2. 针对单词可以进行适当缩写
    3. 采用小写字母加中划线的方式进行命名,尽量不使用下划线或大写字母(看公司需求和个人爱好)
    4. CSS代码的书写顺序遵循CSS样式的渲染顺序
      1. 显示属性 — display、float、position等
      2. 自身属性 — width、height、margin、padding、border
      3. 文本属性 — font-size、line-height、text-align等
      4. 其他(含CSS3等)
  3. JS命名规范与推荐
    1. 标识符命名规范
      1. 区分大小写
      2. 第一个字符必须是一个字母、下划线(_)或一个美元符号($),其他字符可以是字母、下划线、美元符号或数字
      3. 不能含有空格,不能以关键字或保留字命名
    2. 标识符命名推荐
      1. 遵循小驼峰命名法(如:currentTime),除了第一个首字母之外,其他的单词首字母大写
      2. 变量或者属性以名词开头,方法或者函数以动词开头
      3. 常量命名:字母全部大写,多个单词之间试用下划线分割(如:PI_API)
      4. 构造函数名称:首字母大写,遵循大驼峰命名法
  4. 合理的注释与空格
    1. 在绝大多数的操作符前后,需要添加空格
    2. 在数组的逗号后面需要加空格
    3. 在对象属性的都好之后,属性名和属性值分开的冒号前后,均需要加空格
    4. 函数声明的大括号之前(参数括号之后)需要加空格
    5. 函数中,分隔各个参数的逗号之后需要加空格
    6. 合理添加注释
    7. 注释符号与注释内容之间加空格

代码扩展性

学习目标

  • 掌握前后台的数据传递时存在的问题
  • 合理处理文本或图片的超出问题
  • 灵活应用margin负值、伪类选择器等技术,解决代码的扩展性问题
  • 掌握伪元素并能够合理应用
  • 理解a标签的可触区,并能够合理设置
  1. 前后台数据交互的常见问题

    1. 代码扩展性不足引发的问题
      1. 图片尺寸与文本溢出

      2. 为合理处理“相似元素的不同样式”

  2. img标签的宽高与文本超出

    图片、文本问题处理方法

    • 为图片设置合理的宽高

    • 文本超出隐藏

      overflow: hidden;

    • 文本超出省略号

      overflow: hidden;

      text-overflow: ellipsis;

      word-break: keep-all;

      white-space: nowrap;

  3. 代码的扩展性 — 伪类选择器

  4. 代码扩展性 — margin的负值

  5. 伪元素的应用

    • :before 在元素之前添加内容
    • :after 在元素之后添加内容
    • 伪元素的常见用途
      • 实现清浮动
      • 实现背景图
  6. a标签的可触区问题

    • 移动端尽可能大一些,让用户体验更好

页面加载速度

  1. 文件大小、请求次数
  2. 加载方式、代码性能

文件合并与压缩

学习目标

  • 能够输出文件压缩与文件合并,对前端优化的意义
  • 连接传统文件合并与压缩的方法
  • 掌握webpack的使用方法
  • 能够使用webpack实现文件合并于压缩

文件合并、压缩与前端优化的关系

  • 文件合并:涉及服务器请求次数
  • 文件压缩:涉及文件大小

传统文件合并:手动合并

传统文件压缩:借助在线工具

文件压缩与合并 — 新技术 webpack

webpack是一个模块打包器,主要作业是打包JavaScript文件,可借助丰富的插件,实现html、css等其他资源的打包

webpack的安装

  • 安装cnpm(淘宝镜像可选)

    npm install -g cnpm --registry=https://registry.npm.taobao.org

  • 生成package.json文件

    npm init y

  • 局部安装webpack

    npm install -D webpack webpack-cli

打包压缩js文件

新建webpack.config.js

const path = require('path')
module.exports = {
    // 入口
    entry: {
        index: './src/js/index.js',
    },
    // 出口
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, '../out')
    }
    // 模式
    mode: "development"
}

配置package.json

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
     //加入以下代码
    "start": "webpack --config config/webpack.config.js"
  }

运行命令npm run start 便可打包js文件

被依赖的js文件,使用import和export进行处理,引入到响应js文件中,再次打包即可实现js文件的合并

function a() {
    console.log('被依赖的js')
}

export default a;
import a from './inde.js'
a()
console.log('不被依赖的js')

再次输入npm run start 便可合并js文件

HTML文件的压缩

  • 安装插件html-webpack-plugin

    npm install -D html-webpack-plugin

  • webpack.config.js配置文件中引入

    const htmlwpPlug = require('html-webpack-plugin')
    module.exports = {
         plugins: [
            new htmlwpPlug({
                filename: './cn/index.html',
                template: './src/index.html',
                chunks: ['index'],
                minify: {
                    collapseWhitespace: true,
                    removeComments: true
                }
            })
         ]
    }
    
  • 根据插件,调整配置信息后,打包运行

CSS文件压缩、CSS文件独立打包、合并及压缩

  • 安装相关插件css-loaderstyle-loader
  • uglifyjs-webpack-plugin optimize-css-assets-webpack-plugin

修改相应js文件,引入css文件

  • 单独打包安装插件mini-css-extract-plugin

完整webpack.config.js文件

const path = require('path')
const htmlwpPlug = require('html-webpack-plugin')
const UglifyJsPlug = require('uglifyjs-webpack-plugin')
const OptimizeCSSAssetsPlug = require('optimize-css-assets-webpack-plugin')
const MiniCSSExtractPlug = require('mini-css-extract-plugin')

module.exports = {
    entry: {
        index: './src/js/index.js',
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, '../out')
    },
    mode: "development",
    plugins: [
        new htmlwpPlug({
            filename: './cn/index.html',
            template: './src/index.html',
            chunks: ['index'],
            minify: {
                collapseWhitespace: true,
                removeComments: true
            }
        }),
        new MiniCSSExtractPlug({
            filename: "[name].css",
            chunkFilename: "[id].css"
        })
    ],
    optimization: {
        minimizer: [
            new UglifyJsPlug({
                cache: true,
                parallel: true,
                sourceMap: true
            }),
            new OptimizeCSSAssetsPlug({})
        ]
    },
    module:{
        rules: [{
            test: /\.css$/,
            use: [MiniCSSExtractPlug.loader, 'css-loader']
        }]
    }
}

图片与特殊字体

学习目标

  • 能够使用@font-face为文字设置特殊字体
  • 掌握呢书字体的”缩小“方法
  • 能够根据需求为图片选择合理的格式
  • 能够使用在线工具,实现图片压缩与处理
  • 掌握webp与srcset的使用方法
  • 能够利用背景图合并技术,降低服务器请求次数

特殊字体

  1. 传统特殊字体的实现方法与问题

    • 实现方法:截图法
    • 存在的问题:文件过大
    • 传统方法的局限性:内容部分无法使用特殊字体
  2. @font-face的使用

    • @font-face的核心语法
    @font-face{
        font-family: ;
        src:  [][[]]*;
    }
    
    • 字体格式转换:https://transfonter.org
    • @font-face的问题-中文类字体文件较大
  3. 处理特殊字体的工具——fontmin

  4. 不同情境下,对于特殊字体的处理方法

    • “非数据部分”的特殊字体
      • 大量的特殊字体
      • 极少量的特殊字体
    • ”数据部分“,不含中文的特殊字体
    • ”数据部分“,总问类特殊字体

图片

  1. 图片类型

    • gif:全透明或不透明,对像素要求很低的小图标、动画
    • jpg:不透明的,对像素要求一般或较低的图片
    • png-8:半透明或不透明,对像素要救一般的图片
    • png-24:半透明或不透明,对象素要求较高的图片
  2. 图片压缩

    • 压缩原则

    • 在线压缩地址

      智图:https://zhitu.isux.us

      tinypng: https://tinypng.com/

      等等

  3. webp与srcset

    • webp图片类型

    • webp与jpg的比较

    • 转换webp格式的图片的软件/方法

      iSparta软件

      智图

    • 如何实现全浏览器兼容-借助srcset
  4. 背景图片合并技术

    • 背景图和数据图区分方法
    • 什么是背景图合并
    • 为何进行背景图合并
    • 背景图合并的核心原理

文件放置位置

学习目标

  • 能够将CSS、JS文件放置在合理位置
  • 能够说出页面回流与页面重绘

CSS与JS的位置

  1. css代码放置位置
    • 为放置在head标签中的css代码依旧生效
    • css代码的推荐放置位置
  2. js代码放置位置
    • 加js代码放置在顶部的潜在隐患
    • js代码的推荐放置位置

页面回流与重绘

  1. 页面回流

    当render tree(DOM Tree 和样式结构体组合后构建)中的一部分(或全部)因为元素的规模尺寸,布局,隐藏的改变引起的页面重新渲染(或者交做重新构建绘制)

  2. 页面重绘

    当render tree中的一些元素需要更新属性,但这些属性之影响元素的外观,风格,而不会影响到元素的布局,此类页面渲染叫做页面重绘

预加载按需加载和懒加载

学习目标

  • 通过JS实现图片预加载

  • 掌握按需加载的基本原理和实现方法

图片预加载

  1. 图片预加载是什么
  2. 为何要用图片预加载
  3. 图片预加载的核心原理
    • new Image()动态创建img
    • 设置图片src,并使用onload方法回调鱼仔完成的事件
<body>
    <div id="img">div>
    <script>
        var box = document.getElementById('box')
        var img = document.getElementById('img')
        var loadImg = [];
        loadImg.push('img/1.jpg')
        loadImg.push('img/2.jpg')
        loadImg.push('img/3.jpg')
        loadImg.push('img/4.jpg')
        loadImg.push('img/5.jpg')

        var imgsNum = loadImg.length
        var nowNum = 0
        var nowPer = 0  

        for (let i = 0; i < imgsNum; i++) {
            console.log(i)
            var newImg = new Image();
            newImg.src = loadImg[i];
            newImg.onload = (function(){
                nowNum++
                img.appendChild(newImg);
                nowPer = nowNum/imgsNum *100
                console.log(nowPer + '%')
                if (nowNum == imgsNum) {
                    console.log('加载完成')
                    return
                }
            })()
        }
    script>
body>

按需加载

  1. 按需加载HTML内容

    • 核心原理:利用js,在符合某种条件是,将script标签的内容去除,让HTML内容生效
    • 核心代码
    <body>
        <div id="box">
            <script id="hide" type="text/x-template">
                <div>
                    <img src="img/1.jpg" alt="">
                    <img src="img/2.jpg" alt="">
                    <img src="img/3.jpg" alt="">
                </div>
            script>
            <div id="con">点击我看效果div>
        div>
        <script>
            var con = document.getElementById('con')
            var hide = document.getElementById('hide')
            var box = document.getElementById('box')
    
            con.onclick = function(){
                box.innerHTML = hide.innerHTML
                con.onclick = null
            }
        script>
    body>
    
  2. 按需加载图片

    • 核心原理:利用切换图片标签的src属性,实现图片的按需加载
    • 核心代码
    <body>
        <div id="box">
            <div id="tit">点我加载div>
            <div id="con">
                <img src="" alt="图片1" data-src="img/1.jpg" alt="">
                <img src="" alt="图片1" data-src="img/2.jpg" alt="">
            div>
        div>
        <script>
            var imgs = document.getElementById('box').getElementsByTagName('img')
            var tit = document.getElementById('tit')
    
            tit.onclick = function(){
                var imgLen = imgs.length
                for (let i = 0; i < imgLen; i++) {
                    var relSrc = imgs[i].getAttribute('data-src')
                    imgs[i].setAttribute('src',relSrc)
                }
            }
        script>
    body>
    
  3. 按照整个屏幕进行加载

    • 核心原理:将加载的代码放置于textarea当中,再合适的时候使用DOM处理该内容
    • 核心代码
    <textarea id="add1">//具体代码textarea>
    
  4. 利用Ajax实现页面懒加载

    • 核心原理

      在符合某种条件时,触发懒加载

      使用Ajax实现前后台数据交互,一部请求数据

      使用DOM将Ajax异步获取的数据加载到HTML当中

JavaScript代码性能优化

学习目标

  • 掌握标签查找(遍历)此时对代码执行速度的影响
  • 掌握大量DOM节点操做的处理方法
  • 掌握DOM2级时间绑定方法
  • 能够合理使用事件委托
  • 了解利用css与css3属性实现动画的不同之处
  • 掌握函数缓存
  • 能使用惰性载入函数实现DOM2级的事件绑定
  • 能使用函数柯里化实现DOM2级的事件绑定
  • 能借助Canvas,优化图片上传方法

标签查找(遍历)次数

  1. 获取标签

  2. 遍历查找标签列表的长度

    var test = document.getElementById("rest");
    var lists = test.document.getElementsByTsgName("li");
    var len = lists.length;
    for(va i=0; i<len; i++){
        console.log('for中具体代码');
    }
    

大量DOM节点插入时的操作方法

  1. 获取大量数据信息后,DOM节点动态创建方法

    • 方法1:没处理一条,即可创建一个DOM节点,并将DOM节点防止于DOM书当中
    var list = document.getElementById("list");
    for (var i=0; i<2000; i++) {
       list.innerHTML = '
  2. ' + i + '
  3. '
    };
    • 方法2:将所有DOM节点处理完毕之后,一次性将DOM放置于DOM树当中(推荐远快于第一种)
    var list = document.getElementById("list");
    var str = ''
    for (var i=0; i<2000; i++) {
        str += '
  4. ' + i + '
  5. '
    }; list.innerHTML = str;

事件绑定

  • DOM0级 事件绑定问题:同标签 类型事件只能绑定一次

  • DOM2级 事件绑定: 同标签同类型事件可绑定多次

  • DOM2级 事件绑定方法

    • addEventlistener(‘事件名’,函数,true/false);
    • attchEvent(‘on+事件名’,函数);

事件委托

  • 事件委托:减少功能函数的数量

  • 事件委托基本原理

    • 采用DOM2级事件绑定方法
    • 利用事件冒泡的基本原理
    • 检测事件目标对象,执行响应功能函数
// 传统DOM0级事件绑定
var list = document.getElementById("list");
var listItems = list.getElementBytagName("li");
var listlen = listItems.length;
for(var i = 0; i < listlen; i++) {
	listItems[i].onclick = function() {
    	for(var i = 0; i < listlen; i++) {
            listItems[i].className = '';
        }
        this.className = 'select'
	}
}
// 事件委托
list.onclick = function(e) {
    var target = e.target;
    for(var i = 0; i < listlen; i++) {
        listItems[i].className = '';
    };
    target.className = 'select';
}
//DOM2级事件绑定
list.addEventlistener('click',function(e) {
    var target = e.target;
    for(var i = 0; i < listlen; i++) {
        listItems[i].className = '';
    };
    target.className = 'select';
}),false);
  • CSS3属性实现动画性能会比较好

函数缓存

  1. 函数缓存的主要应用场景
    • 解决同参数的多次执行问题
  2. 函数缓存的核心原理
    • 将一个函数对于给定参数的返回值缓存起来
    • 用内存换取性能

DOM2级事件兼容处理方法

  1. 传统实现方法

    <body>
        <div id="btn1">btn1div>
        <div id="btn2">btn2div>
        <script>
            var btn1 = document.getElementById('btn1')
            var btn2 = document.getElementById('btn2')
            // 传统事件绑定
            function eHandler(ele, type, ftn) {
                if (window.addEventListener) {
                    ele.addEventListener(type, function(e){
                        ftn.call(ele, e)
                    },false);
                }else if (window.attachEvent) {
                    ele.attachEvent('on' + type, function (e) {
                        ftn.call(ele, e)
                    })
                }
            }
    
            eHandler(btn1, 'click', function() {
                console.log('元素被点击了')
            });
            eHandler(btn2, 'click', function() {
                console.log('第二个div被点击了')
            });
        script>
    body>
    
  2. 毒性载入函数

    • 核心原理:闭包
    <body>
        <div id="btn1">btn1div>
        <div id="btn2">btn2div>
        <script>
            var btn1 = document.getElementById('btn1')
            var btn2 = document.getElementById('btn2')
            //惰性载入函数
            function eHandler(ele, type, ftn) {
                if (window.addEventListener) {
                    console.log('if语句被执行')
                    eHandler = function(ele, type, ftn){
                        ele.addEventListener(type, function(e){
                            ftn.call(ele, e)
                        },false)
                    }
                }else if (window.attachEvent) {
                    console.log('IE if语句被执行')
                    eHandler = function(ele, type, ftn){
                        ele.attachEvent('on' + type, function(e) {
                            ftn.call(ele, e)
                        })
                    }
                }
                return eHandler(ele, type, ftn)
            }
    
            eHandler(btn1, 'click', function() {
                console.log('元素被点击了')
            });
            eHandler(btn2, 'click', function() {
                console.log('第二个div被点击了')
            });
        script>
    body>
    
  3. 函数柯里化

    • 核心原理:闭包
    <body>
        <div id="btn1">btn1div>
        <div id="btn2">btn2div>
        <script>
            var btn1 = document.getElementById('btn1')
            var btn2 = document.getElementById('btn2')
            //函数柯里化
            var addEvent = (function(){
                if (window.addEventListener) {
                    console.log('是否运行多次')
                    return function(ele, type, ftn){
                        ele.addEventListener(type, function(e){
                            ftn.call(ele, e)
                        },false)
                    }
                }else if (window.attachEvent) {
                    console.log('是否运行多次')
                    return function(ele, type, ftn){
                        ele.attachEvent('on' + type, function(e) {
                            ftn.call(ele, e)
                        })
                    }
                }
            })();
    
            addEvent(btn1, 'click', function() {
                console.log('元素被点击了')
            });
            addEvent(btn2, 'click', function() {
                console.log('第二个div被点击了')
            });
        script>
    body>
    

图片上传

  1. 传统方法实现图片上传
    • 传统实现方法 — 核心原理
      • 使用file类型的input标签
      • 点击之后获取图片路径
      • 将图片加载出来
  2. 传统实现方法存在的问题
  • 加载慢
  1. Canvas优化图片上传

    • canvas实现方法 - 核心原理
      • 使用file类型的input标签
      • 点击之后获取图片路径
      • 通过canvas进行图片绘制
      • 将canvas绘制的图片加载出来
    <body>
        <div>
            <img id="pic" src="" alt="">
        div>
        <div>
            <input type="file" id="fileBtn">
        div>
        <script>
            var pic = document.getElementById('pic')
            var fileBtn = document.getElementById('fileBtn')
            var canvas = document.createElement('canvas')
            var context = canvas.getContext('2d')
            var relPic = new Image()
    
            fileBtn.onchange = function (e) {
                showPic(e)
            }
            function showPic(e) {
                var fileUrl = fileBtn.files[0]
                var fileType = fileUrl.type
                var reader = new FileReader()
                reader.readAsDataURL(fileUrl)
                reader.onload = function(e) {
                    var fileSrc = e.target.result
                    relPic.src = fileSrc
                    relPic.onload = getSize
                }
                function getSize() {
                    var w = this.width
                    var h = this.height
                    var imgSize = 400
                    var cw = w
                    var ch = h
                    if(cw >= ch) {
                        cw = imgSize
                        ch = cw / w * h
                    }else{
                        ch = imgSize
                        cw = ch / h * w
                    }
                    canvas.width = cw
                    canvas.height = ch
                    context.clearRect(0, 0, 400, 400)
                    context.drawImage(this, 0, 0, cw, ch)
    
                    var newImg = canvas.toDataURL(fileType, 0.8)
                    pic.setAttribute('src', newImg)
                }
    
            }
        script>
    body>
    

面向对象

  1. 避免全局作用域被污染
  2. 提升代码可读性
  3. 提升代码复用性
  4. 减少储存空间(函数)的占用

面向对象实现功能需求

  1. 面向对象案例功能需求

    • 创建一个people的基本信息
    • 信息包括用户姓名、用户年龄、用户信息三项内容及一个方法(方法是对用户年龄进行判断,当用户年龄处于不同的范围时,设置不同的用户信息)
    // 面向过程传统方法
    var name = "张三"
    var age = 28
    var info
    function changeInfo() {
    	if (age < 18) {
    		info = name + '未满18岁'
    	}else{
    		info = name + '当前的年龄为:' + age
    	}
    }
    changeInfo()
    console.log(info)
    
  2. 面向对象 — 使用对象实现初步优化

    • 传统实现代码“污染全局作用域”的问题
    • 使用对象,解决全局作用域被污染的问题
    // 面向对象
     var peo = {
    	name: "张三",
        age: 28,
        info: '',
        changeInfo: function() {
    		if (this.age < 18) {
    			this.info = this.name + '未满18周岁'
    		}else {
    			this.info = this.name + '当前的年龄为:' + this.age
    		}
    	}
    }
    peo.changeInfo()
    console.log(peo.info)
    
  3. 面向对象 — 工厂模式

    • 工厂模式
      • 入口——函数参数
      • 加工——具体函数功能
      • 出口——函数返回值
    • 工厂模式的不足之处 — 可读性与空间性问题
    function user(name, age) {
        var peo = {};
        peo.name = name
        peo.age = age
        peo.info = ""
        peo.changeInfo = function() {
            if (this.age < 18) {
                this.info = this.name + '未满18周岁'
            }else {
                this.info = this.name + '当前的年龄为:' + this.age
            }
        }
        return peo
    }
    var peo = user('张三', 28)
    peo.changeInfo()
    console.log(peo.info)
    
  4. 面向对象 — 构造模式与原型模式

    • 构造模式核心原理
      • new实例化对象
      • 将构造函数的作用域赋给新对象(this)
    • 原型模式核心原理
      • 多个实例化对象的方法公用同一个空间
      • 引用类型变量的相关问题
    //构造模式
    function User(name, age) {
        this.name = name
        this.age = age
        this.info = ""
        this.changeInfo = function() {
            if (this.age < 18) {
                this.info = this.name + '未满18周岁'
            }else {
                this.info = this.name + '当前的年龄为:' + this.age
            }
        }
    }
    var peo = new User('张三', 28)
    peo.changeInfo()
    console.log(peo.info)
    
    //原型模式
    function User() {
    
    }
    User.prototype.name = 'user'
    User.prototype.age = 8
    User.prototype.info = ''
    User.prototype.changeInfo = function() {
        if (this.age < 18) {
                this.info = this.name + '未满18周岁'
        }else {
            this.info = this.name + '当前的年龄为:' + this.age
        }
    }
    var peo = new User()
    peo.name = '张三'
    peo.age = 28
    peo.changeInfo()
    console.log(peo.info)
    
  5. 面向对象 — 混合模式

    • 混合模式核心原理
      • 利用构造模式与原型模式各自的优势
      • 构造模式利用参数为每个对象创建相应属性
      • 原型模式实例化后对象的方法公用一个空间
    // 混合模式
    function User(name, age) {
        this.name = name
        this.age = age
        this.info = ""
    }
    User.property.changeInfo = function() {
        if (this.age < 18) {
            this.info = this.name + '未满18周岁'
        }else {
            this.info = this.name + '当前的年龄为:' + this.age
        }
    }
    var peo = new User('张三', 28)
    peo.changeInfo()
    console.log(peo.info)
    

你可能感兴趣的:(笔记,前端,javascript,html,css,node.js,npm)