基础甲骨文

// 1.利用对象字面量创建对象 {}
      // var obj = {};  // 创建了一个空的对象
  var obj = {
        uname: "张三疯",
        age: 18,
        sex: "男",
        sayHi: function () {
          console.log("hi~");
        },
      };
// 方法冒号后面跟的是一个匿名函数
// 2. 使用对象
// (1). 调用对象的属性 我们采取 对象名.属性名 . 我们理解为 的
      console.log(obj.uname);
// (2). 调用属性还有一种方法 对象名['属性名']
      console.log(obj["age"]);
// (3) 调用对象的方法 sayHi   对象名.方法名() 千万别忘记添加小括号
      obj.sayHi();
// 修改:对象名.属性名=新的值
// 添加: 对象名.新属性名=值/方法
// 2 利用 new Object 创建对象  了解
        var obj = new Object(); // 创建了一个空的对象
        obj.uname = '张三疯';
        obj.age = 18;
        obj.sex = '男';
        obj.sayHi = function() {
            console.log('hi~');
        }
        // (1) 我们是利用 等号 = 赋值的方法 添加对象的属性和方法
        // (2) 每个属性和方法之间用 分号结束
        console.log(obj.uname);
        console.log(obj['sex']);
        obj.sayHi();
// 3,利用new 构造函数创建对象;
        function Star(uname, age, sex) {
            this.name = uname;
            this.age = age;
            this.sex = sex;
            this.sing = function(sang) {
                console.log(sang);
            }
        }
        var ldh = new Star('刘德华', 18, '男'); // 调用函数返回的是一个对象
		ldh.speak = (language)=>{console.log('English')}  //新增对象的方法
        // console.log(typeof ldh);
        console.log(ldh.name);
        console.log(ldh['sex']);
        ldh.sing('冰雨');
        var zxy = new Star('张学友', 19, '男');
        console.log(zxy.name);
        console.log(zxy.age);
        zxy.sing('李香兰')
        // 1. 构造函数名字首字母要大写
        // 2. 我们构造函数不需要 return 就可以返回结果
        // 3. 我们调用构造函数 必须使用 new
        // 4. 我们只要new Star() 调用函数就创建一个对象 ldh  {}
        // 5. 我们的属性和方法前面必须添加 this

在构造函数创建对象的过程中,new起关键作用,new主要做了如下事情:
1 创建空对象并让this指向该空对象
2 执行构造函数,给上述空对象增加属性
3 返回已经增加了属性的对象
任何函数都可以是构造函数,函数是不是构造函数由new决定

对象的遍历

// js对象通过 for...in...结构遍历
var obj = {name:'jim',age:20,sex:'男'}
for(var k in obj){
    console.log(k) //会输出3次 name  age  sex
    console.log(obj[k]) // jim  20  男      //k 是变量,必须要用 obj[k] 访问属性值  
}
// in可判断对象中是否存在某个属性 如:
'name' in obj // true
'sayHi' in obj // false

if('sayHi' in obj){
    console.log(obj['sayHi'])
}

js内置对象:

数学对象:
属性、方法名功能
Math.PI圆周率
Math.floor()向下取整
Math.ceil()向上取整
Math.round()四舍五入版 就近取整 注意 -3.5 结果是 -3
Math.abs()绝对值
Math.max()/Math.min()求最大和最小值
Math.random()获取范围在[0,1)内的随机值
//封装 生成min到max之间的随机整数
function getRandom(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min; 
}
日期对象

Date 对象和 Math 对象不一样,Date是一个构造函数,所以使用时需要实例化后才能使用其中具体方法和属性。

// 创建日期对象使用 需要 new Date()
// 1 不传参数得到当前时刻的日期对象
var now = new Date()
// 2 传两个或两个以上参数 表示 年 月 日 ...对应的日期时间对象
var someDay = new Date(2021,3)
var someDay = new Date(2021,3,10)
// 3 传一个日期格式化字符串,表示对应日期/时刻 的日期对象
var anotherDay = new Date('2021/3/20 10:10:10')
// 4 传一个数字型参数,此时该参数被认为是时间戳
var date = new Date(2021) //此处得到的日期对象对应1970/1/1 0:0:2(格林威治时间)

getDay() 获取星期几 0-6 0表示周日

getMonth() 获取月份 0-11 0表示1月

getTime() 获取时间戳 距离格林威治时间1970/1/1 0:0:0 的毫秒数

获取时间戳(某个时刻距离基准时刻的总毫秒数)

// 实例化Date对象
var now = new Date();
// 1. 用于获取对象的原始值
console.log(date.valueOf())	
console.log(date.getTime())	
// 2. 简单写可以这么做
var now = +new Date();			
// 3. HTML5中提供的方法,有兼容性问题
var now = Date.now();

利用时间戳实现倒计时

function addZero(num) {
    return num < 10 ? '0' + num : num;
}
function countDown(futureTime) {
    // 计算目标时刻和当前时刻 的 时间差(间隔的秒数)
    var now = +new Date();
    var future = +new Date(futureTime);
    var time = (future - now) / 1000;

    // 将间隔的秒数 换算成 天 时 分 秒
    var day = parseInt(time / 60 / 60 / 24);
    var hour = addZero(parseInt((time / 60 / 60) % 24));
    var min = addZero(parseInt((time / 60) % 60));
    var sec = addZero(parseInt(time % 60));

    // 将天时分秒 拼接字符串 返回
    return day + '天' + hour + '时' + min + '分' + sec + '秒';
}
console.log(countDown('2021-3-3 19:40:00'));  // 传参加 ''
数组对象

如何判断某个数据是否为数组?

​ 有两种方法 ① 数据 instanceof Array 得到true则说明是数组 ② Array.isArray(数据) 得到true则说明是数组.

Function instanceof Object

数组增加元素?数组中删除元素?

方法名作用参数/返回值备注
push()后面追加元素一个或多个数据 返回 最新数组长度改变原数组
unshift()从前面追加元素一个或多个数据 返回 最新数组长度改变原数组
pop()删除最后一个元素无参数 返回被删除的元素同上
shift()删除第一个元素无参数 返回被删除的元素同上
reverse()翻转数组无 返回反转后的新数组同上
sort()数组排序无参数时字典排序 function(a,b){return a-b}升序排序,return b-a 降序排序同上
indexOf()查找元素参数1:要查找的值
参数2:开始查找的索引
返回索引或-1
不改变
join(‘分隔符’)将数组元素拼接成字符串参数:分隔符,无参数时默认用逗号连接, 返回一个字符串不改变
toString()数组转字符串逗号分隔每一项,返回一个字符串
splice()删除、添加元素splice(开始删除的位置的索引号,删除个数),返回剩余旧元素组成的数组改变
slice()截取数组中的一段slice(begin,end),索引号start到索引号end,end取不到,返回截取的项目组成的新数组不改变
concat()拼接两个数组或往数组中追加元素连接两个或多个数组, 不影响原数组,返回新数组不改变
字符串对象

重要结论:字符串对象方法均不会改变字符串本身------字符串的不可变性

方法名作用参数/返回值备注
indexOf/lastIndexOf()返回字符对应的索引参数 字符 返回值:索引 找不到返回-1
charAt( )返回索引对应的字符参数 索引 返回值:对应的字符str[索引]也能得到
charCodeAt( )返回索引对应的字符的ASCII码参数 索引 返回值:对应的字符的ASCii码
substr( start, length)字符串截取参数 起始位置 截取长度 返回截取的字符串
substring(start, end)字符串截取同slice 但是不接受负值
slice(start, end)字符串截取索引号start到索引号end,end取不到
replace( )字符串替换参数:要替换的旧字符串 新字符串 返回替换后的新字符串默认只替换一次
split(’ ')字符串转数组参数:分隔符 返回 数组
join(’ ')数组转字符串arr.join(‘’) 返回字符串 ’ ‘ 中间设置分隔符

**图片边框:**border-image

  <style>
      .panel {
        border: 15px solid ;
        border-image-width: 51px 38px 20px 132px;
        border-image-source: url(./images/border.png);
        border-image-slice: 51 38 20 132;
      }
      .box {
        width: 800px;
        height: 600px;
        }
      /* border  影响内容边距 */
      /* border-image-width 不影响内容边距 */
      /* 调用类 panel 自带边框 */
  </style>
  
  <body>
    <div class="box panel"></div>
  </body>

API获取元素注意点:

getElementsByTagName(‘标签名’) 、getElementsByClassName(‘类名’) 获取过来的都是伪数组或者空数组[ ];

特殊元素获取:document.body //返回body元素;

​ document.documentElement //返回html元素;

事件源(谁):触发事件的元素;

onfocus-----onblur 获得/失去鼠标焦点时触发;

元素对象.className = ‘类名’ ; 会覆盖元素原先的类; 多类名书写

全选全不选:

input(/this) . checked; 获得表单元素当前选中状态 true/false;

input(/this) . checked = true/false; 设置表单元素当前状态;

button??

获取属性: element.属性 ; element.getAttribute(‘属性’) ; 程序员自己添加的属性称为自定义属性 data-开头

设置属性: element.属性 = ‘值’ ; element.setAttribute(‘属性’, ‘值’); 主要针对于自定义属性

移除属性:removeAttribute(‘属性’)

元素设置索引:元素也是一个对象 element . index = i

节点:

parentNode.children 获取子元素节点集合,使用需要配合 [‘索引’]使用parentNode.children[0];

添加节点 ul.appendChild(’ li ') ul 父级 li 是子级 后面追加元素节点;

添加节点 ul.insertBefore( 新元素li , ul.children[0]); 指定子元素前面添加元素节点;

node.cloneNode(true); 括号为true 深拷贝 复制标签复制里面的内容;

创建元素的三种方式:
<script>
        // 三种创建元素方式区别 
        // 1. document.write() 创建元素  如果页面文档流加载完毕,再调用这句话会导致页面重绘
         var btn = document.querySelector('button');
         btn.onclick = function() {
             document.write('<div>123</div>');
         }

        // 2.1  innerHTML 字符串拼接创建元素  效率最低
        var inner = document.querySelector('.inner');
         for (var i = 0; i <= 100; i++) {
             inner.innerHTML += '<a href="#">百度</a>'
         }
		// 2.2  innerHTML 拼接数组创建元素  效率最高
        var arr = [];
        for (var i = 0; i <= 100; i++) {
            arr.push('<a href="#">百度</a>');
        }
        inner.innerHTML = arr.join('');
        // 3. document.createElement() 创建元素  效率一般
        var create = document.querySelector('.create');
        for (var i = 0; i <= 100; i++) {
            var a = document.createElement('a');
            create.appendChild(a);
        }
    </script>
 var d1/d2 = +new Date();  //测算运行到此处的时间d2-d1
事件注册:
// 1. 传统方式注册事件
// 同一元素,同一事件,后面覆盖前面,前面不执行
    btns[0].onclick = function() {
        alert('hi');
    }
    btns[0].onclick = function() {
            alert('hao a u');
        }
// 2. 事件侦听注册事件 addEventListener 
// (1) 里面的事件类型是字符串 必定加引号 而且不带on
// (2) 同一个元素 同一个事件可以添加多个侦听器(事件处理程序)
// (3)多个事件处理函数依次执行
    btns[1].addEventListener('click', function() {
        alert(22);
    })
    btns[1].addEventListener('click', function() {
        alert(33);
    })
//  document.addEventListener("mousemove", myFunction);
//  document.removeEventListener("mousemove", myFunction); 移出事件
DOM事件流:

捕获–>目标阶段–>冒泡

有些事件没有冒泡:onblur onfocus onmouseenter onmouseleave等

事件对象:

  • this 是事件绑定的元素(绑定这个事件处理函数的元素) 。
  • e.target 是事件触发的元素对象。
  • 通常情况下terget 和 this是一致的,但有一种情况不同,那就是在事件冒泡时(事件委托)。

e.preventDefault(); 阻止默认行为

e.stopPropagation(); 阻止冒泡

鼠标事件对象:

e.clientX / e.clientY 鼠标在浏览器窗口可视区的x和y坐标

e.pageX / e.pageY 鼠标在页面文档的x和y坐标

e.screenX / e.screenY 鼠标在电脑屏幕的x和y坐标

键盘事件:

keydown—> keypress—> keyup 涉及字母大小写用keypress, 不涉及字母大小或者功能键写用keydown/up

e.keyCode === 13 判断用户按下确认键

页面(窗口)加载事件(2种):

window.onload 是窗口 (页面)加载事件,当文档内容完全加载完成会触发该事件;

DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等。

调整窗口大小事件:

window.onresize 是调整窗口大小加载事件, 当触发时就调用处理函数---->响应式布局。

window.innerWidth 当前屏幕的宽度

清除定时器:

timer = setTimeout(function(){} , 延迟的毫秒数) — > clearTimeout(timer);

timer = setInterval(function(){},间隔的毫秒数 ----> clearInterval(timer);

window对象的location属性:

location.href = ‘http://www.itcast.cn’; 跳转页面

location.reload(true); 刷新

location.assign(‘http://www.itcast.cn’); 跳转页面(重定向页面) 记录历史,可实现后退

location.replace(‘http://www.itcast.cn’); 替换当前页面 不记录历史

同步异步–事件循环:

同步异步任务.png

事件循环.png

元素偏移量 offset 系列:

offset偏移量.png

offset
  • offset 可以得到任意样式表中的样式值

  • offset 系列获得的数值是没有单位的

  • offsetWidth 包含padding+border+width 传统盒模型

  • offsetWidth 等属性是只读属性,只能获取不能赋值

  • 所以,我们想要获取元素大小位置,用offset更合适

style
  • style 只能得到行内样式表中的样式值

  • style.width 获得的是带有单位的字符串

  • style.width 获得不包含padding和border 的值

  • style.width 是可读写属性,可以获取也可以赋值

  • 所以,我们想要给元素更改值,则需要用style改变

元素可视区 client 系列:

元素可视区 client 系列.png

元素滚动 scroll 系列:

元素滚动 scroll 系列1.png

元素滚动 scroll 系列2.png

页面滚动事件 scroll
        document.addEventListener('scroll', function() {
            // window.pageYOffset 页面被卷去的头部 })
三大系列区别:

1.offset系列 经常用于获得元素位置 offsetLeft offsetTop

2.client经常用于获取元素大小 clientWidth clientHeight

3.scroll 经常用于获取滚动距离 scrollTop scrollLeft

4.注意 页面 滚动的距离通过 window.pageXOffset 获得

动画函数:
function animate(obj, target, callback) {
    // console.log(callback);  callback = function() {}  调用的时候 callback()
    // 先清除以前的定时器,只保留当前的一个定时器执行
    clearInterval(obj.timer);
    obj.timer = setInterval(function() {
        // 步长值写到定时器的里面
        // 把我们步长值改为整数 不要出现小数的问题
        // var step = Math.ceil((target - obj.offsetLeft) / 10);
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (obj.offsetLeft == target) {
            // 停止动画 本质是停止定时器
            clearInterval(obj.timer);
            // 回调函数写到定时器结束里面
            // if (callback) {
            //     // 调用函数
            //     callback();
            // }
            callback && callback();
        }
        // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
        obj.style.left = obj.offsetLeft + step + 'px';
    }, 15);
}
//  当我们点击了返回顶部模块,就让窗口滚动的页面的最上方
        goBack.addEventListener('click', function() {
            // 里面的x和y 不跟单位的 直接写数字即可
            // window.scroll(0, 0);
            // 因为是窗口滚动 所以对象是window
            animate(window, 0);
        });
网页端轮播图:
window.addEventListener('load', function () {
    //1,获取元素
    var focus = document.querySelector('.focus');
    var last = focus.querySelector('.arrow-l');
    var next = focus.querySelector('.arrow-r');
    var items = focus.querySelectorAll('ul>li');
    var ul = focus.querySelector('ul');
    var ol = focus.querySelector('ol');

    //轮播图宽度
    var w = focus.offsetWidth;
    //当前轮播图索引号给ol小圆圈
    var index = 0;
    //当前小圆点索引
    var circle = 0;
    //2,显示与隐藏
    focus.addEventListener('mouseenter', function () {
        last.style.display = 'block';
        next.style.display = 'block';
        clearInterval(timer);
        timer = null; // 清除定时器变量
    });
    focus.addEventListener('mouseleave', function () {
        last.style.display = 'none';
        next.style.display = 'none';
        timer = setInterval(function() {
            //手动调用点击事件
            next.click();
        }, 1000);
    })
    //3,动态生成ol小圆点:
    for (var i = 0; i < items.length; i++) {
        var li = document.createElement('li');
        // 记录当前小圆圈的索引号 通过自定义属性来做 
        li.setAttribute('index', i);
        ol.appendChild(li);
        //4,小圆点高亮切换:
        li.onclick = function(){
            focus.querySelector('.current').className = '';
            this.className = 'current';
            //4,小圆点播放对应的轮播图  目标位置:索引号*轮播图宽度
            circle = index = this.getAttribute('index');
            // index = this.getAttribute('index');
            animate(ul, - w * index);
        }
    }
    //页面刷新后,手动点击小圆点之前,默认第一个选中
    ol.children[0].className = 'current';
    //5,实现无缝连播,在ul的最后面追加第一个li的克隆
    var temp = items[0].cloneNode(true);
    ul.appendChild(temp);  
    //6,注册点击右箭头播放下一张事件
    next.addEventListener('click', function () {
        index++;
        //当已经播放到了克隆的照片时,直接快速切换到真的第一张,不用animate函数
        if (index > items.length) {
            ul.style.left = 0;
            index = 1;
        }
        animate(ul, - w * index);
        // 小圆点跟随切换
        circle++;
        if (circle > items.length - 1) {
            circle = 0;
        }
        // console.log(index,circle);
        // 取消当前已有的高亮,并为索引号为cricle的小圆点加上高亮
        focus.querySelector('.current').className = '';
        // if (index == 4) {
        //     ol.children[0].className = 'current';
        // } else {
        //     ol.children[index].className = 'current';
        // }
        ol.children[circle].className = 'current';
    })
    //6,注册点击左箭头播放下一张事件
        last.addEventListener('click', function () {
        //当已经播放到了克隆的照片时,直接快速切换到真的第一张,不用animate函数
        if (index == 0) {
            ul.style.left = - 4 * w +'px';
            index = 4;
        }
        index--;
        animate(ul, - w * index);
        // 小圆点跟随切换
        circle--;
        if (circle < 0 ) {
            circle = 3;
        }
        // 取消当前已有的高亮,并为索引号为cricle的小圆点加上高亮
        focus.querySelector('.current').className = '';
        ol.children[circle].className = 'current';
    })
    // 7,鼠标不在轮播图区域时自动播放,鼠标移入轮播图停止自动播放
        var timer = setInterval(function() {
        //手动调用点击事件
        next.click();
    }, 2000);
})
移动端拖动元素:
  1. touchstart、touchmove、touchend可以实现拖动元素
  2. 但是拖动元素需要当前手指的坐标值 我们可以使用 targetTouches[0] 里面的pageX 和 pageY

  3. 移动端拖动的原理: 手指移动中,计算出手指移动的距离。然后用盒子原来的位置 + 手指移动的距离

  4. 手指移动的距离: 手指滑动中的位置 减去 手指刚开始触摸的位置

    拖动元素三步曲:

    (1) 触摸元素 touchstart: 获取手指初始坐标,同时获得盒子原来的位置

    (2) 移动手指 touchmove: 计算手指的滑动距离,并且移动盒子

    (3) 离开手指 touchend:

    注意: 手指移动也会触发滚动屏幕所以这里要阻止默认的屏幕滚动 e.preventDefault();

H5新增classList 属性:
添加类:

element.classList.add(’类名’);

移除类:

element.classList.remove(’类名’);

切换类:

element.classList.toggle(’类名’);

注意:以上方法里面,所有类名都不带点

删除元素本身:

element.remove() --> // remove()方法可以直接删除指定的元素

jQuery:

筛选选择器:

$(li:first) $(‘li:last’) $(‘li:eq(2)’) $(‘li:odd’)奇数 $(‘li:even’)偶数
$(‘li’).parents( )查找所有父级 $(‘li’).parent( )查找亲父级

筛选方法.png

JavaScript高级:

原型对象—对象原型:
      // 1. 构造函数的问题.
      function Star(uname, age) {
        this.uname = uname;
        this.age = age;
        // this.sing = function() {
        //     console.log('我会唱歌');
        // }
      }
      Star.prototype.sing = function () {
        console.log("我会唱歌");
      };
      var ldh = new Star("刘德华", 18);
      var zxy = new Star("张学友", 19);
      console.log(ldh.sing === zxy.sing);
      // 2. 一般情况下,我们的公共属性定义到构造函数里面, 公共的方法我们放到原型对象prototype身上
      // 4,JS对象可分为两大类:普通对象和函数对象 ,所有对象都有__proto__(对象原型)指向构造函数的 prototype 原型对象 --> 构成原型链的属性  终点是null;
      //  所有对象均有原型对象,只有函数对象才有prototype原型对象;
      // 5,函数的  prototype对象  等价于 实例对象的 __proto__;
      // 6, Star.prototype.constructor === ldh.__proto__.constructor  -->都指回构造函数Star

所有函数(Object Array Data…)都可视为Function的实例,Function是他自己的实例:

Function.__proto__ == Function.prototype  // true
数据 instanceof 构造函数   对象类型判断

原型链.png

通过原型为数组扩展内置方法:Array.prototype.sum = function() { }
ES5构造函数复合继承:
function Animal(weight,age){
           this.weight = weight;
           this.age  = age;
       } 
Animal.prototype.breathe = function(){
           console.log('空气真好');
       }
function Dog(weight,age,name,type){
           Animal.call(this,weight,age);   //子构造函数继承父构造函数的属性用   父构造函数.call(this);
           this.name = name;
           this.type = type;
       }
Dog.prototype = new Animal();  //★
    //    Dog.prototype.constructor = Dog;
       Dog.prototype.bark = function(){
           console.log('wangwangwang!');
       }
       var d1 = new Dog(10,3,'小黄','金毛');
       console.log(d1);
       d1.breathe();
       d1.bark();
ES5新增数组方法:

arr . forEach(function(e , i , arr本身){ }) 遍历 无返回值

arr . filter(function(e , i , arr){ return value >= 20; }) 过滤 / 查找 返回新数组,不影响原数组 遍历完每一个e

var arr = [12, 66, 4, 88, 3, 7, 12, 66, 58, 79];
      var newArr1 = arr.filter(function(e,i){
         return arr.indexOf(e) == i
         // 数组去重  arr.indexOf(e)返回 e 对应的索引号 / -1
      })

arr . some(function(e , i , arr){ return value >= 20; }) 查找 返回布尔值 true / false 查找到就终止循环不再继续

arr . every (function(e , i , arr){ return value >= 20; }) 查找 返回布尔值 true / false 全部符合条件才是true 反之false 与some相对

arr.reduce()方法
语法:arr.reduce( Callback , [initialValue可选] )
Callback–>回调函数
接受4个参数(previousValue,currentValue,index,array)
previousValue:上一次reduce调用回调返回的值, reduce第一次执行回调时为初始值(initialValue)
currentValue:数组中当前被处理的元素, index:当前被处理元素在数组中的索引
array:调用reduce方法的数组, initialValue(初始值)
如果提供了初始值[initialValue可选] -->reduce()方法会从索引为0的元素开始执行,返回值初始值为initialValue
如果没有提供初始值reduce()方法会将索引为0的元素当做初始值,从索引为1的元素开始执行

区别:

  • 如果查询数组中唯一的元素, 用some方法更合适, 在some 里面 遇到 return true 就是终止遍历 迭代效率更高
  • 在forEach 里面 return 不会终止迭代
获取对象的属性名:

Object.keys(对象) 获取到当前对象中的属性名 K ,返回值是一个数组

trim ( ) 方法去除字符串两端的空格

修改对象的属性名或者属性值:
Object.defineProperty(对象,修改或新增的属性名,{
		value:修改或新增的属性的值,
		writable:true/false,//如果值为false 不允许修改这个属性值
		enumerable: false,//enumerable 如果值为false 则不允许遍历
        configurable: false  //configurable 如果为false 则不允许删除这个属性 属性是否可以被删除或是否可以再次修改特性
})	
ES6 中新增加了类的概念:

class 关键字,类抽象了对象的公共部分,它泛指某一大类(class),对象特指某一个,通过类实例化一个具体的对象。

类的共有属性放到 constructor 里面,可以接受传递过来的参数,同时返回实例对象,constructor是构造器或者构造函数;只要 new 生成实例时,就会自动调用这个函数。

class Son extends Father{  // 这样子类就继承了父类的属性和方法;
	constructor(x, y) {
    		super(x, y); //使用super调用了父类中的构造函数
        	this.x = x;
   			this.y = y;
    	}
   }
	//子类想要继承父类的方法,同时在自己内部扩展自己的方法,利用super 调用父类的构造函数,super 必须在子类this之前调用
    // 手动调用我们的点击事件  不需要鼠标触发
this.lis[index] && this.lis[index].click();
	//双击事件  ondblclick
that.lis[index].remove();   --> // remove()方法可以直接删除指定的元素

类里面的共有的属性和方法一定要加this使用 ;

ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象

函数内部的this指向:

普通函数调用:函数名() this指向window;

构造函数调用:new 函数名() this指向实例对象;

对象方法调用:对象名. 方法名() this指向该方法所属对象;

事件绑定方法:this指向绑定事件对象;

定时器函数: this指向window;

立即执行函数: this指向window;

var name = "The Window"; 
        var object = {
            name: "My Object",
            getNameFunc: function() {
                // console.log(this);   obj
                return function() {
                    // console.log(this);   window
                    return this.name;
                };
            }
        };
        console.log(object.getNameFunc()())
        
var name = "The Window";  
        var object = {    
            name: "My Object",
            getNameFunc: function() {
                var that = this;
                // console.log(this);      obj
                return function() {
                    return that.name;
                };
            }
        };
        console.log(object.getNameFunc()())
改变函数内部this指向:
fn.call(obj,1,2)//此时的this指向的是对象obj,调用fn函数,参数使用逗号隔开
fn.apply(obj,[1,2])//此时的this指向的是对象obj,调用fn函数,参数使用 [数组] 传递
var f = fn.bind(obj, 1, 2); //此处的f是bind返回的新函数
f();//调用新函数  this指向的是对象obj 参数使用逗号隔开
  1. call 经常做继承.
  2. apply 经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
  3. bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向.
  4. call( ) 只有函数对象才能调用,参数一是修改后的this指向,参数2,参数3…使用逗号隔开连接;作用:调用函数 修改this指向
call()、apply()、bind()方法的相同点及区别?

相同点:

  1. 都是只能被函数调用
  2. 都能改变this指向

区别:

  1. call和apply会直接调用原函数,但是bind只会返回原函数的副本(改变this指向的)

  2. call和bind对原函数参数的传递都是作为第2,3,…n个参数写在call及bind方法的参数列表中,而apply对原函数参数的传递是需要包在一个数组中

    function fn(a,b){return a+b}
    fn.call(window,1,2) //3
    fn.apply(window,[1,2]) //3
    
    Math.max(1,2,3,45)//45
    var x = [4,5,6,7,1]
    Math.max.apply(Math,x)// 7
    
    fn.call(1,2)//NaN 第1个参数必须是this的新指向
    
    function test(){console.log(this)}
    var o = {a:1,b:2}
    test.bind(o,1,2)() //{a:1,b:2}
    test.call(o) //{a:1,b:2}
    test.apply(o) //{a:1,b:2}
    
    call apply bind典型应用场景
    call/apply 构造函数继承,将对象a中的方法应用于对象b
    bind 绑定函数中的this且不能马上调用函数
         给函数传实参,但不马上调用函数
    
严格模式中的变化:
作用:
让js程序更严谨更安全更高效
定义方式:
<script>
    'use strict'
</script>
<script>
function test(){
    'use strict'
}
</script>
注意:只能出现在函数及js脚本的开头
  • 变量相关:

    1. 不能未声明就直接对某个变量赋值

    2. 不能删除已声明的变量

  • this 相关:

    1. 全局函数中的this不再默认指向window而指向undefined,因为该原因构造函数不能没有new直接使用

    2. 定时器回调函数、事件处理函数、对象方法中的this仍遵循谁调用指向谁原则

  • 函数相关:

    1. 禁止使用重名形参

    2. 不能在非函数体的{}中 如 if 结构的{}中定义函数但在{}外调用

高阶函数:

高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。

闭包:

闭包(closure)指 有权访问 另一个函数作用域中变量 的函数。简单理解就是 ,一个作用域可以访问另外一个函数内部的局部变量。 作用:延伸变量的作用范围。

闭包的缺陷:

会导致局部变量资源不被释放,相对而言,更占用资源

闭包的应用:

for (var i = 0; i < lis.length; i++) {
// 利用for循环创建了4个立即执行函数
// 立即执行函数也成为小闭包 因为立即执行函数里面的任何一个函数都可以使用它的i这变量
(function(i) {
    lis[i].onclick = function() {
      console.log(i);
    }
 })(i);
}
//等同于 let 声明变量i 让每个i有自己独立作用域
for (let i = 0; i < lis.length; i++) {
          lis[i].onclick = function () {
            console.log(i);
          };
    }
//3秒钟之后,打印所有li元素的内容
 for (var i = 0; i < lis.length; i++) {
   (function(i) {
     setTimeout(function() {
     console.log(lis[i].innerHTML);
     }, 3000)
   })(i);
}
递归:

如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。即函数内部自己调用自己,使用递归时一定要有退出条件。

// 利用递归函数求斐波那契数列(兔子序列)  1、1、2、3、5、8、13、21...
// 用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值
// 我们只需要知道用户输入的n 的前面两项(n-1 n-2)就可以计算出n 对应的序列值
function f(n) {
  if (n === 1 || n === 2) {
        return 1;
  }
  return f(n - 1) + f(n - 2);
}
console.log(f(6));  //8
递归应用–>对象浅拷贝&深拷贝:
//1, 浅拷贝 只拷贝对象的最外层(最外层简单数据类型更新相互独立),深层级别(复杂数据类型)只拷贝地址,拷贝完后新数据和原数据仍可能会互相干扰
var obj = {
    id: 1,
    name: 'andy',
    msg: {
        age: 18
    }
};
var o = {};
//方法:Object.assign(target,...source)方法可实现浅拷贝
Object.assign(o, obj);
console.log(o);
o.msg.age = 20;
console.log(obj);

//2, 深拷贝
var obj = {
    id: 1,
    name: 'andy',
    msg: {
        age: 18,
        study: {
            grade: 10,
            score: 100,
        },
    },
    color: ['pink', 'red'],
};
 var o = {};
// 封装函数 ---> 利用for...in...遍历对象,instanceof 判断复杂数据类型,再利用递归一层一层遍历下去
function deepCopy(newobj, oldobj) {
    for (var k in oldobj) {
        // 判断我们的属性值属于那种数据类型,数组/对象/简单 判断顺序不能变
        // 1. 获取属性值  oldobj[k]
        var item = oldobj[k];
        // 2. 判断这个值是否是数组,是数组就在新对象里面对应位置生成一个空[ ]
        if (item instanceof Array) {
            newobj[k] = [];
            deepCopy(newobj[k], item);
        } else if (item instanceof Object) {
            // 3. 判断这个值是否是对象, 是对象就在新对象里面对应位置生成一个空{ }
            newobj[k] = {};
            deepCopy(newobj[k], item);
        } else {
            // 4. 属于简单数据类型
            newobj[k] = item;
        }
    }
} 
//   deepCopy(o, obj);
//4,  深拷贝的另一种方式 JSON
o = JSON.parse(JSON.stringify(obj)); 
console.log(o);
正则表达式:

正则表达式也是对象---->表单验证,文本筛选,检索,替换。。。

//调用RegExp对象的构造函数创建 
var regexp = new RegExp(keyword,'g');  参数可以是一个变量,用于检索目标不确定的情形  'i'忽略大小写   'g'全局
//利用字面量创建
var rg = /123/;
正则替换replace替换文本
div.innerHTML = text.value.replace(/激情|gay/g, '**');
 // replace第二个参数可以是一个回调函数
div.innerHTML = text.value.replace(/激情|gay|mm/g, function(e){
       console.log(this);   //window
       console.log(e);   //原字符串被替换的文本
       return e.toUpperCase();   //变成大写
});
// 正则查找★★:
var keyReg = new RegExp(keyword, 'g'); //  相当于 正则  /关键字/g  关键字 为变量
......replace(keyReg,`<b style="color:blue">${keyword}</b>`)

// 修改元素、对象内容★:
	1  var str = document.querySelector('p').innerText;
       str = ''; //会修改p的内容么?  不会

    2  document.querySelector('p').innerHTML = 'xxx'  // 可以修改 
    3  var p = document.querySelector('p');
       p.innerHTML = 'xxx'  // 可以修改

    4  var o = { name: 'zhangsan', age: 30 };
       var n = o.name;
       n = 'lisi';   // 不会修改对象 O
      
       o.name = 'lisi';   // 会修改对象 O

ES6语法:
let关键字

let声明的变量只在所处于的块级有效,使用let/const关键字声明的变量才具有块级作用域;

不存在变量提升;

暂时性死区:利用let声明的变量会绑定在这个块级作用域,不会受外界的影响;

const关键字

声明常量,如果是基本数据类型,不能更改值,如果是复杂数据类型,不能更改地址值。

具有块级作用域;声明常量时必须赋值;

常量赋值后,值不能修改

const PI = 3.14;
PI = 100; // Assignment to constant variable.

const ary = [100, 200];
ary[0] = 'a';
ary[1] = 'b';
console.log(ary); // ['a', 'b']; 
ary = ['a', 'b']; // Assignment to constant variable.
解构赋值(★★★)

ES6中允许从数组/对象中提取值,按照对应位置,对变量赋值。

解构赋值就是把数据结构分解,然后给变量进行赋值

//数组解构
let [a, b, c] = [1, 2, 3, 4, 8]; //a=1  b=2  c=3
//对象解构
let person = { name: 'zhangsan', age: 20 }; 
let { name, age } = person; //name=zhangsan  age=20
let {name: myName, age: myAge} = person;//myName=zhangsan  myAge=20
let {age: myAge} = person; //myAge=20
// 实例:获取到响应数据的父子级
const { data, data: { data: userinfo } } = await getUserProfile()

变量需要跟属性值对应,即变量要小于等于元素/属性个数,对象解构还需要属性名一致

方便的去取对象中的属性跟方法

箭头函数(★★★)

函数体中只有一句代码,且代码的执行结果就是返回值(return省略),可以省略大括号

如果形参只有一个,可以省略小括号

const sum = (num1, num2) => num1 + num2; 
const fn = v => v;

★★★箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this

可以简单理解成,定义箭头函数所在的作用域中this指向谁,箭头函数就指向谁

箭头函数的优点:在于解决了this执行环境所造成的一些问题,如setTimeout和setInterval中使用this所造成的问题

var age = 100;
const obj = {
     age: 20,
     say: () => {
         console.log(this.age);
         console.log(age)
            }
     }
  obj.say();//100 //箭头函数this指向的是被声明的作用域里面,而对象没有作用域的,所以箭头函数虽然在对象中被定义,但是this指向的是全局作用域 
剩余参数(★★)

剩余参数语法允许我们将一个不定数量的参数表示为一个数组,不定参数定义方式,这种方式很方便的去声明不知道参数情况下的一个函数

function sum (first, ...args) {
     console.log(first); // 10
     console.log(args); // [20, 30] 
 }
 sum(10, 20, 30)
------------------------------------------------- 
let students = ['wangwu', 'zhangsan', 'lisi'];
let [s1, ...s2] = students; 
console.log(s1);  // 'wangwu' 
console.log(s2);  // ['zhangsan', 'lisi']
Array 的扩展方法
1,扩展运算符(展开语法):

将数组或者对象转为用逗号分隔的参数序列

 let ary = [1, 2, 3];
 ...ary  // 1, 2, 3
 console.log(...ary);    // 1,2,3,相当于下面的代码
 console.log(1,2,3);
-------------------------------------
//扩展运算符可以应用于合并数组
// 方法一 
 let ary1 = [1, 2, 3];
 let ary2 = [3, 4, 5];
 let ary3 = [...ary1, ...ary2];
 // 方法二 
 ary1.push(...ary2);
-------------------------------------
//将类数组或可遍历对象转换为真正的数组
let oDivs = document.getElementsByTagName('div'); 
oDivs = [...oDivs];
2,构造函数方法:Array.from() :

将伪数组或可遍历对象转换为真正的数组

//定义一个集合
let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
}; 
//转成数组
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
------------------------------------------------------------
//方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理
 let arrayLike = { 
     "0": 1,
     "1": 2,
     "length": 2
 }
 let newAry = Array.from(arrayLike, item => item *2)//[2,4]

注意:如果是对象,那么属性需要写对应的索引

3,实例方法:find()

用于找出“第一个”符合条件的数组成员,如果没有找到返回undefined

let ary = [{
     id: 1,
     name: '张三'
 }, { 
     id: 2,
     name: '李四'
 }]; 
 let target = ary.find((item, index) => item.id == 2);//找数组里面符合条件的值,当数组中元素id等于2的查找出来,注意,只会匹配第一个
console.log(target); // {id: 2, name: '李四'}
4,实例方法:findIndex()

用于找出“第一个”符合条件的数组成员的位置,如果没有找到返回-1

let ary = [1, 5, 10, 15, 15];
let index = ary.findIndex((value, index) => value > 10);
// 相当于let index = ary.indexOf(15);  
console.log(index);  //3
console.log(ary[index]);  //15
5,实例方法:includes()

判断某个数组是否包含给定的值,返回布尔值。

[1, 2, 3].includes(2) // true 
[1, 2, 3].includes(4) // false

6,实例方法:map()

它返回一个新的数组,数组中的元素为原始数组调用函数处理后的值。map()不会对空数组进行检测,不影响原数组,返回一个新数组。

array.map(function(currentValue, [index], [arr]), [thisIndex])
thisValue:可选。对象作为该执行回调时使用,传递给函数,用作"this"的值。
String 的扩展方法
1,模板字符串:

​ 模板字符串中可以解析变量${变量},

​ 可以换行,

​ 可以调用函数${sayHello()}

2,实例方法:startsWith() 和 endsWith()
let str = 'Hello world!';
str.startsWith('Hello') // true 
str.endsWith('!')       // true
3,实例方法:repeat()

string.repeat(n) 方法表示将原字符串重复n次,返回一个新字符串

Set 数据结构(数组去重★★)

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。Set本身是一个构造函数,用来生成 Set 数据结构.

const set = new Set([1, 2, 3, 4, 4]); // {1, 2, 3, 4}
const newArry = [...set]  // [1, 2, 3, 4]  数组去重

arr.filter(function(e,i){
         return arr.indexOf(e) == i
         // 数组去重  arr.indexOf(e)返回 e 对应的索引号 / -1
         // 数组去重 还可以.findIndex(e,i) 返回索引或者-1  ; .includes(e)返回true/false
      })

let arr = [true, 5, 0, false, 6, "", 7, undefined, null, undefined, 1];
console.log(arr.filter((e) => e));  // [true, 5, 6, 7, 1]  数组去空
1,实例方法:

.add(value) ; .delete(value) ; .has(value) ; .clear() ; .forEach() …

Ajax:

基本概念:

URL统一资源定位器(Universal Resource Locator)用于标记网络上某个特定文件位置的特定格式的一个字符串

ajax全称: asynchronous javascript and xml 异步的javacript和xml

ajax其实也是一种请求,通过js代码实现,且发生该请求时页面不会发生整体刷新

ajax缺陷: 无法利用浏览器后退功能;对搜索引擎不友好

实现ajax请求,核心在于浏览器内置的XMLHttpRequest对象

方法名作用注意事项
$.get(url,[data],callback)发起get请求
$.post(url,[data],callback)发起post请求
$.ajax({url:xxx,type:xxx,data:xxx,success:function(){}})发起请求最灵活,参数也最复杂
$(选择器).load(url,callback)
// 注意:每次调用 $.get() 或 $.post() 或 $.ajax() 的之前,
// 会先调用 ajaxPrefilter 这个函数
// 在这个函数中,可以拿到我们给Ajax提供的配置对象
$.ajaxPrefilter(function(options) {
  // 在发起真正的 Ajax 请求之前,统一拼接请求的根路径
  options.url = 'http://ajax.frontend.itheima.net' + options.url
})

接口:所谓接口其实就是一个url地址,通过向该地址发起请求,可以得到相应数据

表单的同步提交:

通过点击 submit 按钮,触发表单提交的操作,从而使页面跳转到 action URL 的行为,叫做表单的同步提交。

<form action="/login" target="_blank" method='post' enctype='multipart/form-data'>
<!-- 表单提交也是一种请求 -->
<!-- action注意:当提交表单后,页面会立即跳转(刷新)到 action 属性指定的 URL 地址 -->
//enctype 的值默认为 application/x-www-form-urlencoded;在涉及到文件上传的操作时,必须将 enctype //的值设置为 multipart/form-data
// 表单同步提交的缺点:
① 页面会发生跳转
② 页面之前的状态和数据会丢失 
//onsubmit(e)  监听表单的提交事件,e 为表单提交的数据对象   .reset() 重置表单

表单.serialize() 方法是 Jquery 中快速获取表单数据的方法,获得的是编码字符串 username=zs&pswd=1111

模板引擎art-template:
  1. 下载art-template模板引擎文件(template-web.js)并引入到html中

  2. 在html中准备 ,且在模板中嵌入各种数据

  3. 调用template(id,data),生成包含数据的html字符串 data对象

  4. 将html字符串加入dom中

    // {{属性名}}  显示属性值不解析标签
    // {{@ 属性名}}  显示属性值解析标签
    ----------------------------------------------------
    {{regTime | dateFormat | test1}}   //过滤器  |  管道符
    ----------------------------------------------------
    // {{if  表达式1}}    
    xxxx              表达式1true时显示
    // {{else if 表达式2}}
    yyyy              表达式2true时显示
    ----------------------------------------------------
    {{each hobby}}
            <li>索引是:{{$index}},循环项是:{{$value}}</li>
    {{/each}}
    
    模板字符串原理:
    var data = {name: '张三'}
    var str = '<div>我是{{name}}</div>'
    var pattern = /{{([a-zA-Z]+)}}/    
    var patternResult = pattern.exec(str)  
    // ['{{name}}', 'name', index: 7, input: '<div>我是{{name}}</div>', groups: undefined]
    
    str = str.replace(patternResult[0], data[patternResult[1]])
    //  console.log(str)  -->  <div>我是张三</div>
    
    xhr对象的基本使用:
    //get方式
    var xhr = new XMLHttpRequest();
    xhr.open('get','http://www.test.cn/api?id=1')
    xhr.send();
    xhr.onreadystatechange = function(){
        if(xhr.readystate==4 && xhr.status==200){
            console.log(xhr.responseText)
        }
    }
    //get请求携带参数的本质:无论使用 $.ajax(),还是使用 $.get(),又或者直接使用 xhr 对象发起 GET 请求,当需要携带参数的时候,本质上,都是直接将参数以查询字符串的形式,追加到 URL 地址的后面,发送到服务器的。
    //post方式
    var xhr = new XMLHttpRequest();
    xhr.open('post','http://www.test.cn/api?id=1')
    xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
    xhr.send('name=zhagnsan&age=20');  //查询字符串
    xhr.onreadystatechange = function(){
        if(xhr.readystate==4 && xhr.status==200){
            console.log(xhr.responseText)
        }
    }
    
    查询字符串及url编码:
    • url中?后面的部份被称为查询字符串

    • http://www.baidu.com?search=c&lang=ch search=c&lang=ch 即属于查询字符串,为url携带的参数,多个参数之间用&分割,get请求发送数据时通过查询字符串发送

    • url中不允许包含非英文字符,浏览器会自动对url进行 ulr编码

    • url编码 API encodeURI(str) url解码 API decodeURI(str)

      var str = "中午我回去吃饭";
      var str2 = encodeURI(str);  //%E4%B8%AD%E5%8D%88%E6%88%91%E5%9B%9E%E5%8...
      
JSON字符串:

​ JSON -> JavaScript Object Notation,即’javascript对象表示法’。

  • 简单来讲就是’javascript对象和数组’的’字符串表示法’。JSON的本质就是’字符串’。

​ JSON 中 数据的类型可以是数字、字符串、布尔值、null、数组、对象6种类型。

JSON.parse(jsonStr); // json反序列化 json字符串转对象/数组
JSON.stringify(obj2); // json序列化 对象/数组转json字符串 
*undefined 或函数不能转化成json字符串,,自动忽略不转化
*JSON数据不能写注释,使用"",不允许''
*最外层必须为[]{}

JSON 的作用:在计算机与网络之间存储和传输数据。

    // 1,数据对象 转 查询字符串   遍历对象 -> push数组 -> .join('&')转查询字符串
    function getQueryStr(obj) {
      var arr = [];
      for (var k in obj) {
        arr.push(k + "=" + obj[k]);
      }
      return arr.join("&");
    }
xhr对象新特性:
  • 设置xhr请求时限 xhr.timeout xhr.ontimeout = function(){//请求超时时触发…}

  • 使用FormData管理表单数据 var fd = new FormData(form)

  • FormData数据不需要url编码*

  • 使用FormData数据发送POST请求,不需要设置Content-Type,不要画蛇添足

  • 支持文件读取及上传 fd.append(‘myfile’,document.querySelecotor(‘input[type=“file”]’).files[0])

  • 支持数据传输进度的获取 xhr.upload.onprogress = function(e){console.log(e.lengthComputable,e.loaded,e.total)} //了解

  • var files = document.querySelector(“#file1”).files; files[0]

  • 文件上传 文本框文件列表对象,伪数据

Axios的基本使用:
    document.querySelector("#btn1").addEventListener("click", function () {
      var url = "http://www.liulongbin.top:3006/api/get";
      var dataObj = { name: "张三", age: 20 };
      axios.get(url, { params: dataObj }).then(function (res) {
        console.log(res.data);
      });
    });

    document.querySelector("#btn2").addEventListener("click", function () {
      var url = "http://www.liulongbin.top:3006/api/post";
      var dataObj = { address: "四川", location: "成都市" };
      axios.post(url, dataObj).then(function (res) {
        console.log(res.data);
      });
    });

    document.querySelector("#btn3").addEventListener("click", function () {
      var url = "http://www.liulongbin.top:3006/api/get";
      var paramsData = { name: "钢铁侠", age: 35 };
      axios({
        method: "GET",
        url: url,
        params: paramsData,
      }).then(function (res) {
        console.log(res.data);
      });
    });

    document.querySelector("#btn4").addEventListener("click", function () {
      var url = "http://www.liulongbin.top:3006/api/post";
      var paramsData = { name: "娃哈哈", age: 18, gender: "女" };
      axios({
        method: "POST",
        url: url,
        data: paramsData,
      }).then(function (res) {
        console.log(res.data);
      });
    });

    document.querySelector("#file").onclick = function () {
      var files = document.querySelector("#test").files;
      if (files.length > 0) {
        // var formData = new FormData();
        // formData.append("avatar", files[0]);
        var formData = { avatar: files[0] };
        axios
          .post("http://www.liulongbin.top:3006/api/post", formData)
          .then(function (res) {
            console.log(res);
          });
      } else {
        alert("请选择文件后再上传!");
      }
    };

多接口创建axios实例.png

同源策略/跨域:
  • 什么是同源?同协议 同域名 同端口
  • 什么是同源策略? 浏览器安全策略之一 指js代码默认只能和同源网页发生资源交互(cookie访问、dom访问、ajax请求等)
  • 什么是跨域?两个非同源的页面之间发生资源交互即存在跨域,跨域页面之间默认无法正常进行ajax请求(请求能发送,目标页面也能接收,但响应数据会被浏览器拦截且报错)

跨域解决方案:

  • 方案1:JSONP 浏览器端js实现,但需服务端配合,只支持get请求
  • 方案2:CORS 服务端实现(node.js php java等)支持get/post请求,是w3c的标准解决方案
JSONP原理:

是通过

前端:
<script>
   // jsonp原理: 动态的创建script标签, 添加src属性 在前段页面中声明一个函数, 从服务器端返回的是函数调用
        function show(data) {
            console.log(data);  // { name: 'zs', age: 22 }
        }
        let spt = document.createElement('script')
        script.src = 'http://127.0.0.1/api/test?callback=show'
        document.body.appendChild(spt)
        setTimeout(function () {
            document.body.removeChild(spt)
        }, 1000)
</script>
------------------------------------------------------------
后端:
app.get('/api/test', (req, res) => {
  // 1. 得到函数的名称
  const funcName = req.query.callback
  // 2. 定义要发送到前端的数据对象
  const data = { name: 'zs', age: 22 }
  // 3. 拼接出一个函数的调用
  const str = `${funcName}(${JSON.stringify(data)})`
  // 4. 把拼接的字符串,响应给前端一个函数调用  show({ name: 'zs', age: 22 })
  res.send(str)
})

// 原理:执行ajax方法时若dataType为'jsonp'则会动态创建script并append到head中,将其src赋值为配置项中url,url后面会追加参数callback=随机函数名 ,随机函数名对应函数即为success函数,接收到接口响应的 随机函数名(数据) 后 执行函数,操作数据; 创建的script完成任务后被动态删除。
防抖:

防抖策略(debounce)是当事件被触发后,延迟 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。有效减 少请求次数,节约请求资源。

//输入框防抖:
var timer = null // 1. 防抖动的 timer
function debounceSearch(keywords) { // 2. 定义防抖的函数
 timer = setTimeout(function() {
 // 发起 JSONP 请求
 getSuggestList(keywords)
 }, 500)
 }
$('#ipt').on('keyup', function() { // 3. 在触发 keyup 事件时,立即清空 timer
 clearTimeout(timer)
// ...省略其他代码
 debounceSearch(keywords)
 })
节流:

节流策略(throttle),顾名思义,可以减少一段时间内事件的触发频率。

① 鼠标连续不断地触发某事件(如点击),只在单位时间内只触发一次;

② 懒加载时要监听计算滚动条的位置,但不必每次滑动都触发,可以降低计算的频率,而不必去浪费 CPU 资源;

节流阀为空,表示可以执行下次操作;不为空,表示不能执行下次操作。

//跟随鼠标移动的天使
$(function() {
 var angel = $('#angel')
 var timer = null // 1.预定义一个 timer 节流阀
 $(document).on('mousemove', function(e) {
 if (timer) { return } // 3.判断节流阀是否为空,如果不为空,则证明距离上次执行间隔不足16毫秒
 timer = setTimeout(function() {
 $(angel).css('left', e.pageX + 'px').css('top', e.pageY + 'px')
 timer = null // 2.当设置了鼠标跟随效果后,清空 timer 节流阀,方便下次开启延时器
 }, 16)
 })
})
总结防抖和节流的区别 / 防抖:

如果事件被频繁触发,防抖能保证只有最有一次触发生效!前面 N 多次的触发都会被忽略!  节流:如果事件被频繁触发,节流能够减少事件触发的频率,因此,节流是有选择性地执行一部分事件!

HTTP协议:
HTTP 请求消息:

1,由请求行(request line)、请求头部( header ) 、空行 和 请求体 4 个部分组成。

a,请求行 请求行由请求方式、URL 和 HTTP 协议版本 3 个部分组成,他们之间使用空格隔开。

b,请求头部 用来描述客户端的基本信息,从而把客户端相关的信息告知服务器。

请求头部由多行 键/值对 组成,每行的键和值之间用英文的冒号分隔。

比如:User-Agent 用来说明当前是什么类型的浏览器; Content-Type 用来描述发送到服务器的数据格式;Accept 用来描述客户端能够接收什么类型的返回内容;

c,空行 最后一个请求头字段的后面是一个空行,通知服务器请求头部至此结束。 请求消息中的空行,用来分隔请求头部与请求体。

d,请求体 中存放的,是要通过 POST 方式提交到服务器的数据。

注意:只有 POST 请求才有请求体,GET 请求没有请求体!
HTTP响应消息:

HTTP响应消息由状态行、响应头部、空行 和 响应体 4 个部分组成。

a,状态行 由 HTTP 协议版本、状态码和状态码的描述文本 3 个部分组成,他们之间使用空格隔开;

分类分类描述
1**信息,服务器收到请求,需要请求者继续执行操作(实际开发中很少遇到 1** 类型的状态码)
2**成功,操作被成功接收并处理
3**重定向,需要进一步的操作以完成请求
4**客户端错误,请求包含语法错误或无法完成请求
5**服务器错误,服务器在处理请求的过程中发生了错误

b,响应头部 用来描述服务器的基本信息。响应头部由多行 键/值对 组成,每行的键和值之间用英文的冒号分隔。

c,空行 在最后一个响应头部字段结束之后,会紧跟一个空行,用来通知客户端响应头部至此结束。 响应消息中的空行,用来分隔响应头部与响应体。

d,响应体 中存放的,是服务器响应给客户端的资源内容。

浏览器地址栏输入URL,按下回车后发生了什么?

在地址栏输入URL后

  1. 输入URL,按回车
  2. dns解析(浏览器是不能识别url地址的,需解析成ip地址),
  3. 建立tcp连接,(tcp三次握手)
  4. 发送Http请求,
  5. 服务器进行处理并返回Http报文,
  6. 浏览器渲染页面,
  7. 断开连接(tcp四次挥手)

1,URL(Uniform Resource Locator),统一资源定位符,用于定位互联网上资源,俗称网址。

域名解析 (DNS解析url对应的ip)

2,域名解析:DNS 协议提供通过域名查找 IP 地址,或逆向从 IP 地址反查域名的服务。DNS 是一个网络服务器,我们的域名解析简单来说就是在 DNS 上记录一条信息记录。
在浏览器中输入URL后,首先要进行DNS解析,DNS解析的顺序为:

1、浏览器缓存
2、本地hosts文件
3、系统缓存
4、路由器缓存
5、DNS服务器迭代查询

分析:域名解析实际上是将域名解析为ip地址的过程。
首先,浏览器会检查本地hosts文件中是否有此url的映射关系,如果有就调用这个ip地址映射,完成域名解析。
如果没有,则会查找本地DNS解析器缓存,如果查找到则返回。
如果还是没有,则会查找本地DNS服务器,如果查找到则返回。
最后迭代查询,按根域服务器 ->顶级域(.com)->第二层域(yeahfei.com) ->子域(admin.yeahfei.com)的顺序找到IP地址。

3,tcp三次握手

1.目的:同步客户端和服务端的序列号和确认号,并交换TCP窗口大小信息。防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
通过DNS得到目标的IP地址后,通过TCP协议向服务器发送请求即三次握手。seq初始序号,客户端是x,服务端是y,

分析:通过第3阶段已经拿到了服务器的ip地址,在获取到IP地址后,便会开始建立一次连接,这是由TCP协议完成的,主要通过三次握手进行连接。

第一次握手: 建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;

第二次握手: 服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手: 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成

4,发送Http请求

建立连接:服务器允许客户端建立连接
接受请求:从网络中读取HTTP报文交给Nginx或者Apache进行规则匹配
处理请求:根据方法,资源,首部和可选的主体部分对请求进行处理
访问资源:寻找存储对象,访问报文中指定的资源
构建响应报文:创建有正确首部的HTTP响应报文

5,服务器处理请求并返回HTTP报文

6,解析渲染页面
浏览器得到页面后会进行展示,如果还包含其他外部资源如图片、视频等等则继续请求其他资源。
分析:如果说响应的内容是HTML文档的话,就需要浏览器进行解析渲染呈现给用户。整个过程涉及两个方面:解析和渲染。在渲染页面之前,需要构建DOM树和CSSOM树。

在浏览器还没接收到完整的 HTML 文件时,它就开始渲染页面了,在遇到外部链入的脚本标签或样式标签或图片时,会再次发送 HTTP 请求重复上述的步骤。在收到 CSS 文件后会对已经渲染的页面重新渲染,加入它们应有的样式,图片文件加载完立刻显示在相的应位置。在这一过程中可能会触发页面的重绘或重排。

1.概念:
重绘:某个元素的背景颜色,文字颜色等,不影响元素周围或内部布局的属性,将只会引起浏览器的重绘。
回流:某个元素的尺寸发生了变化,则需重新计算渲染树,重新渲染。

2.步骤:
根据 HTML 解析出 DOM 树
根据 CSS 解析生成 CSS 规则树
结合 DOM 树和 CSS 规则树,生成渲染树
根据渲染树计算每一个节点的信息

7,断开连接

数据传送完,需断开tcp连接,此时发起tcp四次挥手。
————————————————
原文链接:https://blog.csdn.net/super__code/article/details/103919092

node js:

Node.js 中根据模块来源的不同,将模块分为了 3 大类:内置模块,自定义模块,第三方模块。

1,require() 方法,可以加载需要的内置模块、用户自定义模块、第三方模块进行使用。

模块作用域:和函数作用域类似,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块 作用域。防止变量污染。

2,向外共享模块作用域中的成员:

在每个 .js 自定义模块中都有一个 module 对象,它里面存储了和当前模块有关的信息。

在自定义模块中,可以使用 module.exports 对象,将模块内的成员共享出去,作为对外的接口供外界使用。 外界用 require() 方法导入自定义模块时,得到的就是 module.exports 所指向的对象。

使用 require() 方法导入模块时,导入的结果,永远以 module.exports 指向的对象为准

由于 module.exports 单词写起来比较复杂,为了简化向外共享成员的代码,Node 提供了 exports 对象。默认情况 下,exports 和 module.exports 指向同一个对象。最终共享的结果,还是以 module.exports 指向的对象为准。

加载某个模块,其实是加载该模块的 module.exports 属性。require() 方法用于加载模块。

内置模块:
//fs 模块
const fs = require('fs')
//读文件
fs.readFile("路径", "utf8", function (err, dataStr) {  }//写文件
fs.writeFile('./files/1.txt', str, function(err) {   }//path模块
const path = require('path')
// __dirname  表示当前文件所处的目录
// 注意:  ../ 会抵消前面的路径  ./ 当前目录下
const pathStr = path.join('/a', '/b/c', '../../', './d', 'e')
console.log(pathStr)  // \a\d\e
fs.readFile(path.join(__dirname, './files/1.txt'), 'utf8', function(err, dataStr) {   }---------------------------
const fpath = '/a/b/c/index.html'
const fullName = path.basename(fpath)  // index.html
const nameWithoutExt = path.basename(fpath, '.html')  // index
const fext = path.extname(fpath)  // .html

//http模块 创建服务器基本步骤:
// 1. 导入 http 模块
const http = require('http')
// 2. 创建 web 服务器实例
const server = http.createServer()
// 3. 为服务器实例绑定 request 事件,监听客户端的请求
server.on('request', function (req, res) {
  console.log('Someone visit our web server.')  //有人访问服务器时打印
})
// 4. 启动服务器
server.listen(8080, function () {  
  console.log('server running at http://127.0.0.1:8080') //启动成功打印
})
// req.url 是客户端请求的 URL 地址
// req.method 是客户端请求的 method 类型
// 调用 res.end() 方法,向客户端响应一些内容

自定义模块:

用户创建的每个 .js 文件,都是自定义模块。

使用 require() 加载自定义模块时,必须指定以 ./ 或 …/ 开头的路径标识符。在加载自定义模块时,如果没有指定 ./ 或 …/ 这样的路径标识符,则 node 会把它当作内置模块或第三方模块进行加载。

第三方模块:

Node.js 中的第三方模块又叫做包。

npm平台

初次装包完成后,在项目文件夹下多一个叫做 node_modules 的文件夹和 package-lock.json 的配置文件。

node_modules 文件夹用来存放所有已安装到项目中的包。require() 导入第三方包时,就是从这个目录中查找并加载包。 注意:今后在项目开发中,一定要把 node_modules 文件夹,添加到 .gitignore 忽略文件中。

package-lock.json配置文件用来记录 node_modules 目录下的每一个包的下载信息,例如包的名字、版本号、下载地址等。

package.json npm 规定,在项目根目录中,必须提供一个叫做 package.json 的包管理配置文件。用来记录与项目有关的一些配置 信息。命令–> npm init -y (只能在英文的目录下成功运行!)

当我们拿到一个剔除了 node_modules 的项目之后,运行 npm install 命令(或 npm i)可以一次性安装所有的package.json包管理工具中的依赖包。

删除包 : npm uninstall+包名 ,会把卸载的包,自动从 package.json 的 dependencies 中移除掉。

包分类:全局包

​ 项目包 —> 被安装到项目的 node_modules 目录中的包,分为 开发依赖包 和 核心依赖包:

​ —> 开发依赖包(被记录到 devDependencies 节点中的包,只在开发期间会用到)

​ —> 核心依赖包(被记录到 dependencies 节点中的包,在开发期间和项目上线之后都会用到)

Express

Express作为Web 开发框架第三方包,作用和 Node.js 内置的 http 模块类似,是专门用来创建 Web 服务器的。

req.params / req.query

// 1. 导入 express
const express = require('express')
// 2. 创建 web 服务器
const app = express()

// 4. 监听客户端的 GET 和 POST 请求,并向客户端响应具体的内容
app.get('/user', (req, res) => {
  // 调用 express 提供的 res.send() 方法,向客户端响应一个 JSON 对象
  res.send({ name: 'zs', age: 20, gender: '男' })
})
app.post('/user', (req, res) => {
  // 调用 express 提供的 res.send() 方法,向客户端响应一个 文本字符串
  res.send('请求成功')
})

app.get('/', (req, res) => {
  // 通过 req.query 可以获取到客户端通过查询字符串的形式,发送到服务器的参数:
  // 注意:默认情况下,req.query 是一个空对象
  console.log(req.query)
  res.send(req.query)
})
// 注意:这里的 :id 和 :username是一个动态的参数
app.get('/user/:id/:username', (req, res) => {
  // 通过 req.params 对象,可以访问到 URL 中,通过 : 匹配到的动态参数:
  console.log(req.params)
  res.send(req.params)
})

// 3. 启动 web 服务器
app.listen(80, () => {
  console.log('express server running at http://127.0.0.1')
})
托管静态资源:
app.use(express.static('files'))  // 'files' === './files'

app.use('/files', express.static('./files'))//挂载路径前缀,通过带有 /public 前缀地址来访问
Express 路由:

客户端的请求 与 服务器处理函数 之间的 映射关系

Express 中的路由分 3 部分组成,分别是请求的类型、请求的 URL 地址、处理函数。如下:

​ app.METHOD ( PATH, HANDLER )

路由匹配的注意点

① 按照定义的先后顺序进行匹配

② 请求类型和请求的URL同时匹配成功, 才会调用对应的处理函数

const express = require('express')
const app = express()
// 1. 导入路由模块
const router = require('./03.router')
// 2. 注册路由模块
app.use(router)
// 2. 为路由模块添加统一的访问前缀/api   **
// app.use("/api", router);
// 注意: app.use() 函数的作用,就是来注册全局中间件

app.listen(80, () => {
  console.log('http://127.0.0.1')
})
---------------------------------------------------
    // 这是路由模块  另一个JS文件
// 1. 导入 express
const express = require('express')
// 2. 创建路由对象
const router = express.Router()

// 3. 挂载具体的路由
router.get('/user/list', (req, res) => {
  res.send('this is list')
})
router.post('/user/add', (req, res) => {
  res.send('this is add')
})

// 4. 向外导出路由对象
module.exports = router
Express 中间件

中间件 本质上就是一个 function (req,res,next) { } 处理函数

注意

① 一定要在路由之前注册中间件

② 客户端发送过来的请求,可以连续调用多个中间件进行处理

③ 中间件函数的形参列表中,必须包含 next 参数,而路由处理函数中只包含 req 和 res

④ 为了防止代码逻辑混乱,调用 next() 函数后不要再写额外的代码

⑤ 连续调用多个中间件时,多个中间件之间,共享 req 和 res 对象

局部生效的中间件/全局生效的中间件

使用const定义的中间件,只在当前路由中生效的中间,叫做局部生效的中间件。

通过调用 app.use(中间件函数),客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件。

// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()

// 1. 定义局部中间件函数
const mw1 = (req, res, next) => {
  console.log('调用了局部生效的中间件')
  next()
}
// 定义全局中间件函数
app.use((req, res, next) => {
  console.log("调用了全局中间件");
  next();
});

// 2. 创建路由
app.get('/', mw1, (req, res) => {     //调用了局部中间件+全局中间件
  res.send('Home page.')
})
app.get('/user', (req, res) => {      //只调用了全局中间件
  res.send('User page.')
})

// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})
应用级别的中间件:

通过 app.use() 或 app.get() 或 app.post() ,绑定到 app 实例上的中间件,叫做应用级别的中间件。

路由级别的中间件:

绑定到 express.Router() 实例上的中间件,叫做路由级别的中间件。

错误级别的中间件:

专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。

格式:错误级别中间件的 function 处理函数中,必须有 4 个形参,形参顺序从前到后,分别是 (err, req, res, next)。

注意:错误级别的中间件, 必须注册在所有路由之后!

Express内置的中间件:

① express.static 快速托管静态资源的内置中间件,例如: HTML 文件、图片、CSS 样式等(无兼容性)

② express.json 解析 JSON 格式的请求体数据(有兼容性,仅在 4.16.0+ 版本中可用)

③ express.urlencoded 解析 URL-encoded 格式的请求体数据

第三方的中间件:

安装–>require导入—>调用app.use()注册使用

数据库:

作用:保存数据;

组织结构:库(database) 一般一个网站对应一个库

->表(table)

->行(row)

->列(column)

安装phpstudy集成包 启动 MySQL

安装navicat -->文件–>新建连接–>MySQL–>端口 localhost / 127.0.0.1 密码 root

数据类型:

int :整数 char 内存大小不可变的字符串 varchar 内存大小可变的字符串 …

数据属性:

AI 自动增长 pk 主键 uq 值唯一 nn 不能为空

SQL语句:
对数据库里的数据进行 增删改查:

关键字大小写不敏感)

1,查 select语句 --> 查询所有列和所有行的数据 --> select * from 表名称

​ --> 查询指定字段 --> select 列名称,列名称… from 表名称

2,增 insert into语句 --> 增加数据 --> insert into 表名 (字段名1,字段名2…) values (‘字段1的值’,‘字段1的值’…)

​ a,新增完整的一条数据时,字段省略,字段的值对应完整的表的字段,自增数据用null代替

​ insert into users values (null,‘tom’,‘20’,‘0’)

​ b,根据字段名插入数据 ----> insert into users (username,age) values (‘jerry’,‘2’)

​ c,一次插入多条数据 ----> insert into users (username,age,status) values (‘je’,‘2’,‘1’),(‘rry’,‘2’,‘1’)

3,改 update语句 --> 更新数据 --> update users(表名) set age = 100,status=2 where id=2 **where切记

4,删 delete语句 --> 删除数据 --> delete from users(表名) where id=2 **其余字段数据的id不受影响

​ delete from users(表名) 删除整个表单数据,之前id继续占用不清零

​ truncate users 清空整个表单,之前id清零,新增数据的自增属性(id)从0开始

where子句:

​ select * from users where id>3&&status=0

​ select * from users where id<=3||status=1

​ select * from users where username<>‘tom’ username 不等于 tom 的所有用户

order by子句:数据排序

​ select * from users order by status === select * from users order by status asc 升序排序

​ select * from users order by id desc 降序排序

高级查询:多重条件

​ select * from users order by status asc,id desc 多重条件排序用逗号链接条件

​ select * from users where status=1 order by id desc 查找+排序用空格链接条件

​ limit: 限定查询条数

​ select * from users limit 3,2 从第3条数据开始(不包括第三条),查询2个

​ -->应用分页

数学统计:

– 总和 count(*) – 最大值 max(age) – 最小值 min(age) – 平均值 avg(age)

select count(*) from users

– 查询字段重命名 as

select username as name from users

node.js操作数据库…

前后端身份认证:
1,两种开发模式:

服务端渲染 推荐使用Session认证机制

前后端分离渲染 推荐JWT认证机制

2,HTTP协议的无状态性:

浏览器的每次HTTP请求都是独立的,连续多个请求之间没有之间的关系,浏览器没有记忆功能,不会主动保留每次HTTP请求的状态。突破 HTTP 无状态的限制:Cookie Session token

3,Cookie认证:

现实生活中的"会员卡"身份认证方式,在web开发中的专业术语叫做 Cookie.

存储在用户浏览器中的一般不超过4KB的字符串,它由一个名称(Name)、一个值(Value)和其它几个用 于控制 Cookie 有效期、安全性、使用范围的可选属性组成。

不同域名下的 Cookie 各自独立,每当客户端发起请求时,会自动把当前域名下所有未过期的 Cookie 一同发送到服务器(通过添加请求头Cookie的方式),请求成功,浏览器响应会添加一个响应头Set-Cookie

Cookie.png

Cookie的几大特性: ① 自动发送 ② 域名独立 ③ 过期时限 ④ 4KB 限制

客户端存储的是明文,浏览器也提供了读写 Cookie 的 API,因此 Cookie 很容易被伪造。

4,Session认证:

基于Cookie,“会员卡 + 刷卡认证”的设计理念,就是 Session 认证机制的精髓。

Session.png

5,Express 中使用 Session 认证:

安装 / 配置 / app.use()注册 express-session 全局中间件

Session 认证机制需要配合 Cookie 才能实现。由于 Cookie 默认不支持跨域访问,所以,当涉及到前端跨域请求后端接 口的时候,需要做很多额外的配置,才能实现跨域 Session 认证。

6, JWT认证:

JWT(英文全称:JSON Web Token)是目前最流行的跨域认证解决方案。 前端,后端为两个以上服务器。

JWT.png

JWT原理.png

总结:用户的信息通过 Token 字符串的形式,保存在客户端浏览器中。服务器通过还原 Token 字符串的形式来认证用户的身份。

JWT 的三个组成部分,从前到后分别是 Header、Payload、Signature。

⚫ Payload 部分才是真正的用户信息,它是用户信息经过加密之后生成的字符串。

⚫ Header 和 Signature 是安全性相关的部分,只是为了保证 Token 的安全性。

ES6模块化规范:

什么是ES6模块化规范?
node.js 遵循了 CommonJS 的模块化规范。其中:

⚫ 导入其它模块使用 require() 方法

⚫ 模块对外共享成员使用 module.exports 对象

AMD 和 CMD 适用于浏览器端的 Javascript 模块化 (前端)

CommonJS 适用于服务器端的 Javascript 模块化 (后端)

基于node.js使用ES6模块化语法,需要在package.json 的根节点中添加 “type”: “module” 节点

—> 大一统的 ES6 模块化规范

ES6 模块化规范中定义:

  1. 每个 js 文件都是一个独立的模块
  2. 导入其它模块成员使用 import 关键字
  3. 向外共享模块成员使用 export 关键字
默认导出/导入:

默认导出的语法: export default 默认导出的成员 —》向外共享模块中的成员

默认导入的语法: import 接收变量 from ‘模块标识符/路径’ —》接收其他 JS 模块中的成员

  1. 接收变量–>同声明变量命名规则 为一个对象

  2. 每个模块中,只允许使用唯一的一次 export default,否则会报错!

  3. 模块标识符/路径–> 可以是内置模块,第三方模块,自定义模块(必须以 ./ 开头)
按需导出/导入:

按需导出的语法: export 按需导出的成员

按需导入的语法: import { s1,s2 } from ‘模块标识符’

按需导出与按需导入的注意事项 :

① 每个模块中可以使用多次按需导出

② 按需导入的 成员名称s1 必须和按需 导出的名称s1 保持一致

③ 按需导入时,可以使用 as 关键字进行重命名,旧名称无法继续使用

④ 按需导入 可以和 默认导入 一起使用 默认导入+按需导入

import * as 接收变量 from ‘模块标识符/路径’

—> *** 全部**,必须配合as重命名一个接收变量

直接导入并执行模块中的代码:

只想单纯地执行某个模块中的代码(如css),并不需要得到模块中向外共享的成员–>

直接导入, improt ‘模块标识符/路径’ 即可,无需导出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值