Js的组成

  • ECMAscript:Javascript语法
  • DOM:页面文档对象模型
  • BOM浏览器对象模型

    Js输入输出语句

    | alert(msg) | 浏览器弹出警示框 | | —- | —- | | console.log(msg) | 浏览器控制台打印输出信息 | | prompt(info) | 浏览器弹出输入框,用户可以输入 |

变量

本质:变量是程序在内存中申请的一块用来存放数据的空间。

var

使用 var 声明的变量存在于最近的函数或全局作用域中,没有块级作用域的机制。
没有块作用域很容易污染全局,下面函数中的变量污染了全局环境。

  1. function run() {
  2. web = "houdunren";
  3. }
  4. run();
  5. console.log(web); //houdunren

没有块作用作用域时var也会污染全局。

  1. for (var i = 0; i < 10; i++) {
  2. console.log(i);
  3. }
  4. console.log(i);

使用let有块作用域时则不会

  1. let i = 100;
  2. for (let i = 0; i < 6; i++) {
  3. console.log(i);
  4. }
  5. console.log(i);

下例中体验到 var 没有块作用域概念, do/while 定义的变量可以在块外部访问到

  1. var num = 0;
  2. function show() {
  3. var step = 10;
  4. do {
  5. var res = 0;
  6. console.log(num = step++);
  7. res = num;
  8. } while (step < 20);
  9. console.log(`结果是${res}`);
  10. }
  11. show();

var 全局声明的变量也存在于 window对象中

  1. var hd = "houdunren";
  2. console.log(window.hd); //houdunren

以往没有块任用时使用立即执行函数模拟块作用域

  1. (function() {
  2. var $ = this.$ = {};
  3. $.web = "后盾人";
  4. }.bind(window)());
  5. console.log($.web);

有了块作用域后实现就变得简单多了

  1. {
  2. let $ = (window.$ = {});
  3. $.web = "后盾人";
  4. }
  5. console.log($.web);

let

var 声明的区别是 let/const 拥有块作用域,下面代码演示了块外部是无法访问到let声明的变量。
建议将let在代码块前声明。
用逗号分隔定义多个。
let存在块作用域特性,变量只在块域中有效。

  1. if (true) {
  2. let web = 'hdcms',url = 'houdunren.com';
  3. console.log(web); //hdcms
  4. }
  5. console.log(web); //web is not defined

块内部是可以访问到上层作用域的变量

  1. if (true) {
  2. let user = "向军大叔";
  3. (function() {
  4. if (true) {
  5. console.log(`这是块内访问:${user}`);
  6. }
  7. })();
  8. }
  9. console.log(user);

每一层都是独立作用域,里层作用域可以声明外层作用域同名变量,但不会改变外层变量

  1. function run() {
  2. hd = "houdunren";
  3. if (true) {
  4. let hd = "hdcms";
  5. console.log(hd); //hdcms
  6. }
  7. console.log(hd); //houdunren
  8. }
  9. run();

const

使用 const 用来声明常量,这与其他语言差别不大,比如可以用来声明后台接口的URI地址。

  • 常量名建议全部大写
  • 只能声明一次变量
  • 声明时必须同时赋值
  • 不允许再次全新赋值
  • 可以修改引用类型变量的值
  • 拥有块、函数、全局作用域

常量不允许全新赋值举例。

  1. try {
  2. const URL = "https://www.houdunren.com";
  3. URL = "https://www.hdcms.com"; //产生错误
  4. } catch (error) {
  5. throw new Error(error);
  6. }

改变常量的引用类型值。

  1. const INFO = {
  2. url: 'https://www.houdunren.com',
  3. port: '8080'
  4. };
  5. INFO.port = '443';
  6. console.log(INFO);

下面演示了在不同作用域中可以重名定义常量

  1. const NAME = '后盾人';
  2. function show() {
  3. const NAME = '向军大叔';
  4. return NAME;
  5. }
  6. console.log(show()); //输出向军大叔
  7. console.log(NAME); //输出后盾人

重复定义

使用 var 可能造成不小心定义了同名变量

  1. //优惠价
  2. var price = 90;
  3. //商品价格
  4. var price = 100;
  5. console.log(`商品优惠价格是:${price}`);

使用let 可以避免上面的问题,因为let声明后的变量不允许在同一作用域中重新声明

  1. let web = 'houdunren.com';
  2. let web = '后盾人'; //Identifier 'web' has already been declared

不同作用域可以重新声明

  1. let web = 'houdunren.com';
  2. if (true) {
  3. let web = '后盾人'; //Identifier 'web' has already been declared
  4. }

但可以改变值这是与const不同点

  1. let price = 90;
  2. price = 88;
  3. console.log(`商品价格是:${price}`);

let 全局声明的变量不存在于 window对象中,这与var声明不同

  1. let hd = "hdcms";
  2. console.log(window.hd); //undefined

TDZ

TDZ 又称暂时性死区,指变量在作用域内已经存在,但必须在let/const声明后才可以使用。
TDZ可以让程序保持先声明后使用的习惯,让程序更稳定。
变量要先声明后使用。
建议使用let/const 而少使用var。
使用let/const 声明的变量在声明前存在临时性死区(TDZ)使用会发生错误

  1. console.log(x); // Cannot access 'x' before initialization
  2. let x = 1;

run函数作用域中产生TDZ,不允许变量在未声明前使用。

  1. hd = "houdunren";
  2. function run() {
  3. console.log(hd);
  4. let hd = "hdcms";
  5. }
  6. run();

下面代码b没有声明赋值不允许直接使用

  1. function hd(a = b, b = 3) {}
  2. hd(); //Cannot access 'b' before initialization

因为a已经赋值,所以b可以使用a变量,下面代码访问正常

  1. function hd(a = 2, b = a) {}
  2. hd();

锁死变量不允许修改的方法

  1. <script>
  2. const HOST = {
  3. url:"http://www.houdunren.com/api",
  4. port:443
  5. };
  6. Object.freeze(HOST); //用了这个方法之后 7行语句就修改不了端口了
  7. HOST.port = 80;
  8. console.log(HOST);
  9. </script>

传值和传址

  1. <script>
  2. //传值 两个变量独立储存
  3. let a = 1;
  4. let b = a;
  5. //传址 两个变量存同一个地址
  6. let e = {};
  7. let f = e;
  8. </script>

或与非

逻辑与

用&&表示。
举例:a&&b(其中a、b都代表一个条件)
如果a和b都为真,则结果为真,如果a和b中有一来个条件为假,则结果为假。

逻辑或

用||表示。
举例:a||b(其中a、b都代表一个条件)
如果a和b有一个或以上为真,则结果为真,二者都为假知时,结果为假。

逻辑非

用!表示。
举例:!a(a代表一个条件)
如果a为假,则!a为真,如果a为真,则!a为假。

filter函数

filter函数的使用

  1. let lessons = [
  2. {title:'媒体查询响应式布局',category:'css'},
  3. {title:'FLEX弹性盒模型',category:'css'},
  4. {title:'MYSQL多表查询随意操作',category:'mysql'}
  5. ];
  6. const csslessons = lessons.filter(function(lesson){
  7. return lesson['category'] == 'css';
  8. });
  9. console.table(csslessons);

原理

  1. let hd = [1,2,3,4,5];
  2. function filter(array,callback){
  3. let newArray = [];
  4. for (const value of array) {
  5. if(callback(value) === true){
  6. newArray.push(value);
  7. }
  8. }
  9. return newArray;
  10. }
  11. console.table(
  12. filter(hd,function(value){
  13. return value > 2;
  14. })
  15. ); //3,4,5

箭头函数

箭头函数表达式没有自己的this,arguments,super或new.target。

  • 引入箭头函数的作用:更简短的函数并且不绑定this
    1. let sum = (x,y,z) => { return x+y+z; } //不绑定this
    不绑定this
    在箭头函数出现之前,每个新定义的函数都有他自己的this值。(在构造函数的情况下是一个新对象,在严格模式的函数调用中为 undefined,如果该函数被作为“对象方法”调用则为基础对象等)。
    通过call、apply调用箭头函数
    由于箭头函数没有自己的this指针,通过call()、apply()方法调用时,第一个参数会被忽略。(箭头函数中若用了this,这个this指向包裹箭头函数的第一个普通函数的 this。)
    不绑定arguments
    大多数情况下,使用剩余参数是相较于arguments对象的更好选择。
    箭头函数不能使用new操作符
    箭头函数不能用作构造器,和 new一起用会抛出错误。
    1. var Foo = () => {};
    2. var foo = new Foo(); // TypeError: Foo is not a constructor
    箭头函数没有prototype属性
    箭头函数不能用作生成器
    yield 关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)。因此,箭头函数不能用作生成器。
    返回对象字面量时
    箭头函数返回简单值时可以简写成:
    1. let sum = ( x, y ) => x + y
    but返回对象字面量时不口以这样var func = () => {foo: 1},需要用圆括号括起来
    1. var func = () => ({foo: 1});

    字符类型

    弱类型

    在JS中变量类型由所引用的值决定。
    1. var web = "hdcms";
    2. console.log(typeof web); //string
    3. web = 99;
    4. console.log(typeof web); //number
    5. web = {};
    6. console.log(typeof web); //object

    undefined和null的用法

    null表示”没有对象”,即该处不应该有值。典型用法是:**
  1. 作为函数的参数,表示该函数的参数不是对象。
  2. 作为对象原型链的终点。

    1. Object.getPrototypeOf(Object.prototype)
    2. // null

    undefined表示”缺少值”,就是此处应该有一个值,但是还没有定义。典型用法是:

  3. 变量被声明了,但没有赋值时,就等于undefined。

  4. 调用函数时,应该提供的参数没有提供,该参数等于undefined。
  5. 对象没有赋值的属性,该属性的值为undefined。
  6. 函数没有返回值时,默认返回undefined。
    1. var i;
    2. i // undefined
    3. function f(x){console.log(x)}
    4. f() // undefined
    5. var o = new Object();
    6. o.p // undefined
    7. var x = f();
    8. x // undefined

    语句

    label

    标签(label) 为程序定义位置,可以使用continue/break跳到该位置。
    下面取i+n 大于15时退出循环
    1. houdunren: for (let i = 1; i <= 10; i++) {
    2. hdcms: for (let n = 1; n <= 10; n++) {
    3. if (n % 2 != 0) {
    4. continue hdcms;
    5. }
    6. console.log(i, n);
    7. if (i + n > 15) {
    8. break houdunren;
    9. }
    10. }
    11. }

    for/in

    用于遍历对象的所有属性,for/in主要用于遍历对象,不建议用来遍历数组。
    遍历数组操作
    1. let hd = [
    2. { title: "第一章 走进JAVASCRIPT黑洞", lesson: 3 },
    3. { title: "ubuntu19.10 配置好用的编程工作站", lesson: 5 },
    4. { title: "媒体查询响应式布局", lesson: 8 }
    5. ];
    6. document.write(`
    7. <table border="1" width="100%">
    8. <thead>
    9. <tr>
    10. <th>标题</th>
    11. <th>课程数</th>
    12. </tr>
    13. </thead>
    14. `);
    15. for (let key in hd) {
    16. document.write(`
    17. <tr>
    18. <td>${hd[key].title}</td>
    19. <td>${hd[key].lesson}</td>
    20. </tr>
    21. `);
    22. }
    23. document.write("</table>");
    遍历对象操作
    1. let info = {
    2. name: "后盾人",
    3. url: "houdunren.com"
    4. };
    5. for (const key in info) {
    6. if (info.hasOwnProperty(key)) {
    7. console.log(info[key]);
    8. }
    9. }
    遍历window对象的所有属性
    1. for (name in window) {
    2. console.log(window[name]);
    3. }

    for/of

    用来遍历 Arrays(数组), Strings(字符串), Maps(映射), Sets(集合)等可迭代的数据结构。
    for/in 不同的是 for/of 每次循环取其中的值而不是索引。
    1. let arr = [1, 2, 3];
    2. for (const iterator of arr) {
    3. console.log(iterator);
    4. }
    遍历字符串
    1. let str = 'houdunren';
    2. for (const iterator of str) {
    3. console.log(iterator);
    4. }
    使用迭代特性遍历数组(后面章节会介绍迭代器)
    1. const hd = ["hdcms", "houdunren"];
    2. for (const [key, value] of hd.entries()) {
    3. console.log(key, value); //这样就可以遍历了
    4. }
    使用for/of 也可以用来遍历DOM元素
    1. <body>
    2. <ul>
    3. <li></li>
    4. <li></li>
    5. </ul>
    6. </body>
    7. <script>
    8. let lis = document.querySelectorAll("li");
    9. for (const li of lis) {
    10. li.addEventListener("click", function() {
    11. this.style.backgroundColor = "red";
    12. });
    13. }
    14. </script>

类型检测

JS提供了非常丰富的数据类型,开发者要学会使用最适合的数据类型处理业务 。

typeof

typeof 用于返回以下原始类型

  • 基本类型:number/string/boolean
  • function
  • object
  • undefined

可以使用typeof用于判断数据的类型

  1. let a = 1;
  2. console.log(typeof a); //number
  3. let b = "1";
  4. console.log(typeof b); //string
  5. //未赋值或不存在的变量返回undefined
  6. var hd;
  7. console.log(typeof hd);
  8. function run() {}
  9. console.log(typeof run); //function
  10. let c = [1, 2, 3];
  11. console.log(typeof c); //object
  12. let d = { name: "houdunren.com" };
  13. console.log(typeof d); //object

instanceof

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
也可以理解为是否为某个对象的实例,typeof不能区分数组,但instanceof则可以。

后面章节会详细介绍原型链

  1. let hd = [];
  2. let houdunren = {};
  3. console.log(hd instanceof Array); //true
  4. console.log(houdunren instanceof Array); //false
  5. let c = [1, 2, 3];
  6. console.log(c instanceof Array); //true
  7. let d = { name: "houdunren.com" };
  8. console.log(d instanceof Object); //true
  9. function User() {}
  10. let hd = new User();
  11. console.log(hd instanceof User); //true

值类型与对象

下面是使用字面量与对象方法创建字符串,返回的是不同类型。

  1. let hd = "houdunren";
  2. let cms = new String("hdcms");
  3. console.log(typeof hd, typeof cms); //string object

只有对象才有方法使用,但在JS中也可以使用值类型调用方法,因为它会在执行时将值类型转为对象。

  1. let hd = "houdunren";
  2. let cms = new String("hdcms");
  3. console.log(hd.length); //9
  4. console.log(cms.length); //5

模板字面量

使用 ... 符号包裹的字符串中可以写入引入变量与表达式

  1. let url = 'houdunren.com';
  2. console.log(`后盾人网址是${url}`); //后盾人网址是houdunren.com

支持换行操作不会产生错误

  1. let url = 'houdunren.com';
  2. document.write(`后盾人网址是${url}
  3. 大家可以在网站上学习到很多技术知识`);

使用表达式

  1. function show(title) {
  2. return `后盾人`;
  3. }
  4. console.log(`${show()}`)

模板字面量支持嵌套使用
Js - 图1

  1. let lessons = [
  2. {title: '媒体查询响应式布局'},{title: 'FLEX 弹性盒模型'},{title: 'GRID 栅格系统'}
  3. ];
  4. function template() {
  5. return `<ul>
  6. ${lessons.map((item)=>`
  7. <li>${item.title}</li>
  8. `).join('')}
  9. </ul>`;
  10. }
  11. document.body.innerHTML = template();

标签模板

标签模板是提取出普通字符串与变量,交由标签函数处理

  1. let lesson = 'css';
  2. let web = '后盾人';
  3. tag `访问${web}学习${lesson}前端知识`;
  4. function tag(strings, ...values) {
  5. console.log(strings); //["访问", "学习", "前端知识"]
  6. console.log(values); // ["后盾人", "css"]
  7. }

下面例子将标题中有后盾人的使用标签模板加上链接
Js - 图2

  1. let lessons = [
  2. { title: "后盾人媒体查询响应式布局", author: "后盾人向军" },
  3. { title: "FLEX 弹性盒模型", author: "后盾人" },
  4. { title: "GRID 栅格系统后盾人教程", author: "古老师" }
  5. ];
  6. function links(strings, ...vars) {
  7. return strings
  8. .map((str, key) => {
  9. return (
  10. str +
  11. (vars[key]
  12. ? vars[key].replace(
  13. "后盾人",
  14. `<a href="https://www.houdunren.com">后盾人</a>`
  15. )
  16. : "")
  17. );
  18. })
  19. .join("");
  20. }
  21. function template() {
  22. return `<ul>
  23. ${lessons
  24. .map(item => links`<li>${item.author}:${item.title}</li>`)
  25. .join("")}
  26. </ul>`;
  27. }
  28. document.body.innerHTML += template();

字符串

获取长度

使用length属性可以获取字符串长度

  1. console.log("houdunren.com".length)

大小写转换

将字符转换成大写格式

  1. console.log('houdunren.com'.toUpperCase()); //HOUDUNREN.COM

转字符为小写格式

  1. console.log('houdunren.com'.toLowerCase()); //houdunren.com

移除空白

使用trim删除字符串左右的空白字符

  1. let str = ' houdunren.com ';
  2. console.log(str.length);
  3. console.log(str.trim().length);

使用trimLeft删除左边空白,使用trimRight删除右边空白

  1. let name = " houdunren ";
  2. console.log(name);
  3. console.log(name.trimLeft());
  4. console.log(name.trimRight());

获取单字符

根据从0开始的位置获取字符

  1. console.log('houdunren'.charAt(3))

使用数字索引获取字符串

  1. console.log('houdunren'[3])

截取字符串

使用 slice、substr、substring 函数都可以截取字符串。

  • slice、substring 第二个参数为截取的结束位置
  • substr 第二个参数指定获取字符数量

    1. let hd = 'houdunren.com';
    2. console.log(hd.slice(3)); //dunren.com
    3. console.log(hd.substr(3)); //dunren.com
    4. console.log(hd.substring(3)); //dunren.com
    5. console.log(hd.slice(3, 6)); //dun
    6. console.log(hd.substring(3, 6)); //dun
    7. console.log(hd.substring(3, 0)); //hou 较小的做为起始位置
    8. console.log(hd.substr(3, 6)); //dunren
    9. console.log(hd.slice(3, -1)); //dunren.co 第二个为负数表示从后面算的字符
    10. console.log(hd.slice(-2));//om 从末尾取
    11. console.log(hd.substring(3, -9)); //hou 负数转为0
    12. console.log(hd.substr(-3, 2)); //co 从后面第三个开始取两个

    查找字符串

    从开始获取字符串位置,检测不到时返回 -1

    1. console.log('houdunren.com'.indexOf('o')); //1
    2. console.log('houdunren.com'.indexOf('o', 3)); //11 从第3个字符向后搜索

    从结尾来搜索字符串位置

    1. console.log('houdunren.com'.lastIndexOf('o')); //11
    2. console.log('houdunren.com'.lastIndexOf('o', 7)); //1 从第7个字符向前搜索

    search() 方法用于检索字符串中指定的子字符串,也可以使用正则表达式搜索

    1. let str = "houdunren.com";
    2. console.log(str.search("com"));
    3. console.log(str.search(/\.com/i));

    includes 字符串中是否包含指定的值,第二个参数指查找开始位置

    1. console.log('houdunren.com'.includes('o')); //true
    2. console.log('houdunren.com'.includes('h', 11)); //true

    startsWith 是否是指定位置开始,第二个参数为查找的开始位置。

    1. console.log('houdunren.com'.startsWith('h')); //true
    2. console.log('houdunren.com'.startsWith('o', 1)); //true

    endsWith 是否是指定位置结束,第二个参数为查找的结束位置。

    1. console.log('houdunren.com'.endsWith('com')); //true
    2. console.log('houdunren.com'.endsWith('o', 2)); //true

    下面是查找关键词的示例

    1. const words = ["php", "css"];
    2. const title = "我爱在后盾人学习php与css知识";
    3. const status = words.some(word => {
    4. return title.includes(word);
    5. });
    6. console.log(status);

    替换字符串

    replace 方法用于字符串的替换操作

    1. let name = "houdunren.com";
    2. web = name.replace("houdunren", "hdcms");
    3. console.log(web);

    默认只替换一次,如果全局替换需要使用正则(更强大的使用会在正则表达式章节介绍)

    1. let str = "2023/02/12";
    2. console.log(str.replace(/\//g, "-"));

    使用字符串替换来生成关键词链接

    1. const word = ["php", "css"];
    2. const string = "我喜欢在后盾人学习php与css知识";
    3. const title = word.reduce((pre, word) => {
    4. return pre.replace(word, `<a href="?w=${word}">${word}</a>`);
    5. }, string);
    6. document.body.innerHTML += title;

    使用正则表达式完成替换

    1. let res = "houdunren.com".replace(/u/g, str => {
    2. return "@";
    3. });
    4. console.log(res);

    Symbol

    用Symbol解决字符串耦合的问题

    1. let user1 = {
    2. name:'李四',
    3. key:Symbol()
    4. };
    5. let user2 = {
    6. name:'李四',
    7. key:Symbol()
    8. };
    9. let grade = {
    10. [user1.key] : { js : 99,css : 98},
    11. [user2.key] : { js : 11,css : 32}
    12. };
    13. console.log(grade);

    set

    获取判断删除查看清空元素

    1. let set = new Set(['hdcms','houdunren']);
    2. let set = new Set(['hdcms']); //'h','d','c','m','s'
    3. console.log(set.size); //获取元素数量
    4. console.log(set.has('hdcms')); //判断是否有此元素
    5. set.delete('hdcms'); //删除元素
    6. set.values(); //查看元素的值
    7. set.clear(); //彻底清空

    使用set处理网站关键词

    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    6. <title>Document</title>
    7. </head>
    8. <body>
    9. <input type="text" name="hd" id="">
    10. <ul></ul>
    11. <script>
    12. let obj = {
    13. data:new Set(),
    14. set keyword(word){
    15. this.data.add(word);
    16. },
    17. show(){
    18. let ul = document.querySelector('ul');
    19. ul.innerHTML = '';
    20. this.data.forEach(function(value){
    21. ul.innerHTML += `<li>${value}</li>`;
    22. });
    23. }
    24. };
    25. let input = document.querySelector("[name= 'hd']");
    26. input.addEventListener('blur',function(){
    27. obj.keyword = this.value;
    28. obj.show();
    29. });
    30. </script>
    31. </body>
    32. </html>

    用set实现并集交集差集

    1. <script>
    2. let set1 = new Set([1,2,3,4,5,6]);
    3. let set2 = new Set([2,4,5,7,8,9]);
    4. //并集
    5. console.log(new Set([...set1,...set2]));
    6. //差集
    7. console.log(
    8. new Set(
    9. [...set1].filter(function(item){
    10. return !set2.has(item);
    11. })
    12. )
    13. );
    14. //交集
    15. console.log(
    16. new Set(
    17. [...set1].filter(function(item){
    18. return set2.has(item);
    19. })
    20. )
    21. );
    22. </script>

    WeakSet

    添加删除判断元素

    1. <script>
    2. // let set = new WeakSet('hdcms'); 报错 只能加引用值
    3. // let set = new WeakSet(['hdcms','houdunren']); 报错 只能加引用值
    4. // set.add(['hdcms','houdunren']); 不报错
    5. let nodes = new WeakSet();
    6. let divs = document.querySelectorAll('div');
    7. divs.forEach(function(item){
    8. nodes.add(item);
    9. });
    10. console.log(nodes);
    11. nodes.delete(divs[0]); // 移除元素
    12. console.log(nodes.has('hdcms')); //判断元素
    13. </script>

    方法

    splice和splice的增删改查

    1. let arr = [1,2,3,4,5];
    2. let hd = arr.splice(1,2); //截取2,3
    3. let hd = arr.splice(1); //截取2,3,4,5 原数组不变
    4. let hd = arr.splice(0,2); //截取1,2 原数组改变
    5. let hd = arr.splice(0,2,'aaa','bbb'); //aaa,bbb,1,2
    6. let hd = arr.splice(1,1,'后盾人'); //1,后盾人,3,4,5
    7. let hd = arr.splice(2,0,'后盾人'); //1,2,后盾人,3,4,5
    8. let hd = arr.splice(arr.length,0,'后盾人1','后盾人2'); //1,2,3,4,5,后盾人1,后盾人2
    9. let hd = arr.splice(0,0,'后盾人1','后盾人2'); //后盾人1,后盾人2,1,2,3,4,5
    10. console.log(hd);

    Array.from()方法

    Array.from()方法就是将一个类数组对象或者可遍历对象转换成一个真正的数组。
    那么什么是类数组对象呢?所谓类数组对象,最基本的要求就是具有length属性的对象。

  • 将类数组对象转换为真正数组:

    1. let arrayLike = {
    2. 0: 'tom',
    3. 1: '65',
    4. 2: '男',
    5. 3: ['jane','john','Mary'],
    6. 'length': 4
    7. }
    8. let arr = Array.from(arrayLike)
    9. console.log(arr) // ['tom','65','男',['jane','john','Mary']]

    那么,如果将上面代码中length属性去掉呢?实践证明,答案会是一个长度为0的空数组。
    这里将代码再改一下,就是具有length属性,但是对象的属性名不再是数字类型的,而是其他字符串型的,代码如下:

    1. let arrayLike = {
    2. 'name': 'tom',
    3. 'age': '65',
    4. 'sex': '男',
    5. 'friends': ['jane','john','Mary'],
    6. length: 4
    7. }
    8. let arr = Array.from(arrayLike)
    9. console.log(arr) // [ undefined, undefined, undefined, undefined ]

    会发现结果是长度为4,元素均为undefined的数组。
    由此可见,要将一个类数组对象转换为一个真正的数组,必须具备以下条件:

  1. 该类数组对象必须具有length属性,用于指定数组的长度。如果没有length属性,那么转换后的数组是一个空数组。
  2. 该类数组对象的属性名必须为数值型或字符串型的数字。ps: 该类数组对象的属性名可以加引号,也可以不加引号
  • 将Set结构的数据转换为真正的数组:

    1. let arr = [12,45,97,9797,564,134,45642]
    2. let set = new Set(arr)
    3. console.log(Array.from(set)) // [ 12, 45, 97, 9797, 564, 134, 45642 ]

    Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。如下:

    1. let arr = [12,45,97,9797,564,134,45642]
    2. let set = new Set(arr)
    3. console.log(Array.from(set, item => item + 1)) // [ 13, 46, 98, 9798, 565, 135, 45643 ]
  • 将字符串转换为数组:

    1. let str = 'hello world!';
    2. console.log(Array.from(str)) // ["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "!"]
  • Array.from参数是一个真正的数组:

    1. console.log(Array.from([12,45,47,56,213,4654,154]))

    像这种情况,Array.from会返回一个一模一样的新数组。

    map()的使用

    概念

    map()方法定义在JavaScript的Array中,它返回一个新的数组,数组中的元素为原始数组调用函数处理后的值。
    注意:map()不会对空数组进行检测。
    map()不会改变原始数组。

    语法

    1. array.map(function(currentValue, index, arr), thisIndex)

    参数说明:

currentValue 必须 当前元素的的值。
index 可选 当前元素的索引。
arr 可选 当前元素属于的数组对象。
thisValue 可选 对象作为该执行回调时使用,传递给函数,用作”this”的值。

实例

  1. //返回由原数组中每个元素的平方组成的新数组:
  2. let array = [1, 2, 3, 4, 5];
  3. let newArray = array.map((item) => {
  4. return item * item;
  5. })
  6. console.log(newArray) // [1, 4, 9, 16, 25]

find和findIndex

find

  1. let arr = [1,2,3,4,5];
  2. let res = arr.find(function(item){
  3. //只要return true 就就近原则返回元素
  4. return true; //1
  5. return item == 2; //2
  6. return item == 200; //undefined
  7. });
  8. console.log(res);

findIndex

  1. let lessons = [{name:'js'},{name:'css'},{name:'mysql'}];
  2. let index = lessons.findIndex(function(item){
  3. return item.name == 'css';
  4. });
  5. console.log(index);

findIndex原理

  1. function find(array,callback){
  2. for(const value of array){
  3. if(callback(value)) return value;
  4. }
  5. return undefined;
  6. }
  7. let arr = [1,2,3,4,5];
  8. console.log(
  9. find(arr,function(item){
  10. return item == 2;
  11. })
  12. );

排序sort

  1. let arr = [1,2434,346,87,3523];
  2. arr = arr.sort(function(a,b){
  3. //-1 从小到大排 1从大到小排
  4. return a-b;
  5. });
  6. console.log(arr); //1, 87, 346, 2434, 3523

实例

重复生成

下例是根据参数重复生成星号

  1. function star(num = 3) {
  2. return '*'.repeat(num);
  3. }
  4. console.log(star());

下面是模糊后三位电话号码

  1. let phone = "98765432101";
  2. console.log(phone.slice(0, -3) + "*".repeat(3));

取随机数

  1. function arrayRandomValue(array,start = 1,end){
  2. end = end ? end :array.length;
  3. start --;
  4. const index = start + Math.floor(Math.random() * (end - start));
  5. return array[index];
  6. }
  7. console.log(arrayRandomValue([1123,123,123,42,324,234]));

计算脚本时间

  1. const start = Date.now();
  2. for(let i = 0 ;i < 20000; i ++){
  3. }
  4. const end = Date.now();
  5. console.log(end - start);

向数组末尾再追加一个数组

  1. //迭代的方法
  2. const hd = ['1','2','3'];
  3. const hdd = ['4','5'];
  4. for(const value of hdd){
  5. hd.push(value);
  6. }
  7. console.log(hd);
  8. //点语法
  9. const hd = ['1','2','3'];
  10. const hdd = ['4','5'];
  11. hd = [...hd,...hdd];//...是展开数组的意思
  12. console.log(hd);

数组移动函数实例

  1. function move(array,from,to){
  2. if(from < 0 ||to > array.length){
  3. console.error('参数错误');
  4. return;
  5. }else{
  6. const newArray = [...array];
  7. let item = newArray.splice(from,1);
  8. newArray.splice(to,0,...item);
  9. return newArray;
  10. }
  11. }
  12. let array = [1,2,3,4];
  13. console.log(move(array,-1,3));

购物车排序

  1. let cart = [
  2. {name:'ipone',price:12000},
  3. {name:'imac',price:10000},
  4. {name:'ipad',price:1232},
  5. {name:'xiaomi',price:1111}
  6. ];
  7. cart = cart.sort(function(a,b){
  8. return b.price - a.price;
  9. });
  10. console.table(cart);

reduce

统计元素出现次数

  1. let arr = [1,2,3,1,1];
  2. function arrayCount(array,item){
  3. return array.reduce(function(total,cur){
  4. total += item == cur?1:0;
  5. return total;
  6. },0);
  7. }
  8. console.log(arrayCount(arr,1));

求价格最大的商品

  1. let carts = [
  2. {name:'iphone',price:10000},
  3. {name:'ipad',price:20000},
  4. {name:'ipod',price:30000},
  5. ];
  6. function cartMax(cart){
  7. return cart.reduce(function(pre,cur){
  8. return pre.price > cur.price ? pre : cur;
  9. });
  10. }
  11. console.table(cartMax(carts));

求购物车总价

  1. let carts = [
  2. {name:'iphone',price:10000},
  3. {name:'ipad',price:20000},
  4. {name:'ipod',price:30000},
  5. ];
  6. function sum(cart){
  7. return cart.reduce(function(total,cur){
  8. return (total += cur['price']);
  9. },0);
  10. }
  11. console.log(sum(carts));

获取价格超过10000元的商品名称

  1. let carts = [
  2. {name:'iphone',price:1232},
  3. {name:'ipad',price:23432},
  4. {name:'ipod',price:12312},
  5. ];
  6. function getNameByPrice(goods,price){
  7. return goods
  8. .reduce(function(arr,cur){
  9. if(cur.price > price){
  10. arr.push(cur);
  11. }
  12. return arr;
  13. },[])
  14. .map(item => item.name);
  15. }
  16. console.table(getNameByPrice(carts,10000));

去重

  1. let arr = [1,2,3,4,1,2,5];
  2. let newArr = arr.reduce(function(arr,cur){
  3. if(arr.includes(cur) === false){
  4. arr.push(cur);
  5. }
  6. return arr;
  7. },[]);
  8. console.table(newArr);

购物车去重

  1. let carts = [
  2. {name:'iphone',price:100000},
  3. {name:'iphone',price:100000},
  4. {name:'iphone',price:100000},
  5. {name:'iphone',price:100000},
  6. {name:'iphone',price:100000},
  7. {name:'iphone',price:100000}
  8. ];
  9. function filterGoods(goods){
  10. return goods.reduce(function(arr,cur){
  11. let find = arr.find(function(v){
  12. return v.name == cur.name;
  13. });
  14. if(!find) arr.push(cur);
  15. return arr;
  16. },[]);
  17. }
  18. console.log(filterGoods(carts));

LOGO动画效果 css与js结合

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>Document</title>
  7. </head>
  8. <style>
  9. *{
  10. margin:0;
  11. padding: 0;
  12. }
  13. body{
  14. width: 100vh;
  15. height: 100vh;
  16. display: flex;
  17. justify-content: center;
  18. align-items: center;
  19. background-color: #34495e;
  20. }
  21. div{
  22. font-size: 3em;
  23. font-weight: bold;
  24. text-transform: uppercase;
  25. color: #9b59b6;
  26. }
  27. div > span{
  28. position: relative;
  29. display: inline-block;
  30. }
  31. .color{
  32. animation-name: color;
  33. animation-duration: 1s;
  34. animation-iteration-count: 2;
  35. animation-timing-function: linear;
  36. animation-direction: alternate;
  37. }
  38. @keyframes color{
  39. 50%{
  40. color: #f1c40f;
  41. transform: scale(2);
  42. }
  43. to{
  44. color: #e74c3c;
  45. transform: scale(0.5);
  46. }
  47. }
  48. </style>
  49. <body>
  50. <div>houdunren.com</div>
  51. <script>
  52. const div = document.querySelector('div');
  53. [...div.textContent].reduce(function(pre,cur,index){
  54. pre == index && (div.innerHTML = '');
  55. let span = document.createElement('span');
  56. span.innerHTML = cur;
  57. div.appendChild(span);
  58. span.addEventListener('mouseover',function(){
  59. this.classList.add('color');
  60. });
  61. span.addEventListener('animationend',function(){
  62. this.classList.remove('color');
  63. })
  64. },0);
  65. </script>
  66. </body>
  67. </html>

QQ截图20200518085119.png

tab选项卡

1.页面布局

分别设置五个标签,给五个标签写序号,移到标签上获取标签序号显示对应的div

1)设置一个整个的div包含所有的标签和所有内容

2)标签

  1. <ul>
  2. <li style="background: blanchedalmond;">快餐</li>
  3. <li style="background: burlywood;">面食</li>
  4. <li style="background: cyan;">甜点</li>
  5. </ul>

3)内容

  1. <div class="main" style="background: blanchedalmond;"></div>
  2. <div class="main" style="background: burlywood;"></div>
  3. <div class="main" style="background: cyan;"></div>

2.页面样式

1)设置整个tab的大小边框

  1. #tab{
  2. width: 400px;
  3. height: 300px;
  4. border: blue 1px solid;
  5. margin: 30px auto;
  6. }

2)标签浮动\清除标签原点

  1. #tab ul li{
  2. float: left;
  3. width: 133px;
  4. height: 30px;
  5. list-style: none;
  6. text-align: center;
  7. line-height: 30px;
  8. }

3)设置内容部分,加绝对定位避免出现三个内容框,设置内容部分隐藏

  1. .main{
  2. width: 390px;
  3. height: 250px;
  4. margin-left: 5px;
  5. margin-top: 40px;
  6. display: none;
  7. position: absolute;
  8. }

3.给标签加移入事件,实现tab切换

1)获取id进行 定义

2)利用循环给li分别加序号

  1. for(var i=0;i<list.length;i++){
  2. list[i].xuhao=i;
  3. }

3)鼠标移入事件:获取li的序号,显示对应序号的div

  1. list[i].onmouseover=function(){
  2. var c=this.xuhao;
  3. cn[c].style.display="block"
  4. }

4)保证tab可以循环使用,每执行一次都先让其他都隐藏在显示对应的

  1. for(var i=0;i<3;i++){
  2. cn[i].style.display="none";
  3. }

4.源码

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title></title>
  6. <style>
  7. *{
  8. padding: 0;
  9. margin: 0;
  10. }
  11. #tab{
  12. width: 400px;
  13. height: 300px;
  14. border: blue 1px solid;
  15. margin: 30px auto;
  16. }
  17. /*标签浮动\清除原点*/
  18. #tab ul li{
  19. float: left;
  20. width: 133px;
  21. height: 30px;
  22. list-style: none;
  23. text-align: center;
  24. line-height: 30px;
  25. }
  26. /*设置内容部分,加绝对定位避免出现三个内容框*/
  27. .main{
  28. width: 390px;
  29. height: 250px;
  30. margin-left: 5px;
  31. margin-top: 40px;
  32. display: none;
  33. position: absolute;
  34. }
  35. </style>
  36. </head>
  37. <body>
  38. <div id="tab">
  39. <!--标签-->
  40. <ul>
  41. <li style="background: blanchedalmond;">快餐</li>
  42. <li style="background: burlywood;">面食</li>
  43. <li style="background: cyan;">甜点</li>
  44. </ul>
  45. <!--内容-->
  46. <div class="main" style="background: blanchedalmond;display: block;"></div>
  47. <div class="main" style="background: burlywood;"></div>
  48. <div class="main" style="background: cyan;"></div>
  49. </div>
  50. <script>
  51. var tab=document.getElementById("tab");
  52. var list=tab.getElementsByTagName("li");
  53. var cn=tab.getElementsByClassName("main");
  54. // 加序号
  55. for(var i=0;i<list.length;i++){
  56. list[i].xuhao=i;
  57. // 鼠标移入事件
  58. list[i].onmouseover=function(){
  59. // 获得当前移入的li序号
  60. var c=this.xuhao;
  61. for(var i=0;i<3;i++){
  62. cn[i].style.display="none";
  63. }
  64. // 对应序号的内容显示
  65. cn[c].style.display="block"
  66. }
  67. }
  68. </script>
  69. </body>
  70. </html>

class类的语法

通过class关键字,可以定义类
ES6 的类,可以看作构造函数的另一种写法

  1. class Constructor {
  2. // ...
  3. }
  4. typeof Constructor // "function"
  5. Constructor === Constructor.prototype.constructor // true 原型内存指针

上面代码表明,类的数据类型就是函数,类本身就指向构造函数
使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。

  1. class App {
  2. method() {
  3. console.log('Alaing');
  4. }
  5. }
  6. var m = new App();
  7. m.method() // "Alaing"
  8. m.constructor === App.prototype.constructor // true

在类的实例上调用方法,就是调用原型上的方法。

m 是 App 的实例,它的constructor方法就是B类原型的constructor方法。

由于类的方法都定义在prototype对象上面,所以类的新方法可以添加在prototype对象上面。Object.assign 方法可以很方便地一次向类添加多个方法。

  1. class App {
  2. constructor(){
  3. // ...
  4. }
  5. }
  6. Object.assign(App.prototype, {
  7. toString(){},
  8. toValue(){},
  9. });

Object.assign()

基本用法

  1. Object.assign()//方法用于对象的合并,将源对象(option)的所有可枚举属性,复制到目标对象(root)
  2. const option1 = { b: 2 }
  3. const option2 = { c: 3 }
  4. Object.assign(root, source1, source2);
  5. root // {a:1, b:2, c:3}
  6. Object.keys()// 返回对象自身的所有可枚举的属性的键名。

constructor 方法
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有定义,constructor方法会被默认添加。

static “静态方法”
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

  1. class KInd {
  2. static classMethod() {
  3. return 'Aliang';
  4. }
  5. }
  6. KInd.classMethod() // 'Aliang'
  7. var kind = new KInd();
  8. kind.classMethod()
  9. // TypeError: kind.classMethod is not a function

KInd类的 classMethod 方法前有static关键字,表明该方法是一个静态方法,可以直接在KInd类上调用(KInd.classMethod()),而不是在KInd类的实例上调用。如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。

注意:如果静态方法包含this关键字,这个this指的是类,而不是实例。

父类的静态方法,可以被子类继承。

  1. class Parent {
  2. static classMethod() {
  3. return 'Aliang';
  4. }
  5. }
  6. class Bar extends Parent {
  7. }
  8. Bar.classMethod() // 'Aliang'

上面代码中,父类Parent有一个静态方法,子类Bar可以调用这个方法

静态方法也是可以从super对象上调用。

  1. class Parent {
  2. static classMethod() {
  3. return 'This is Parent method';
  4. }
  5. }
  6. class Son extends Parent {
  7. static classMethod() {
  8. return super.classMethod() + ', This is Son method';
  9. }
  10. }
  11. Son.classMethod() // "This is Parent method, This is Son method"

class 继承
Class 可以通过 extends 关键字实现继承,这比 ES5 的通过修改原型链实现继承要清晰和方便。

  1. class Parent {
  2. }
  3. class Son extends Parent {
  4. }

上面代码定义了一个Son类,该类通过extends关键字,继承了Parent类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Parent类。

super 关键字
super 这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。

第一种情况,super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数

  1. class Panent {}
  2. class Son extends Panent {
  3. constructor() {
  4. super();
  5. }
  6. }
  7. const m = new Son()
  8. m //Panent {}proto: Son constructor: class B

上面代码中,子类Son的构造函数之中的super(),代表调用父类的构造函数。这是必须的,否则 JavaScript 引擎会报错。

注意:super虽然代表了父类Panent的构造函数,但是返回的是子类Son的实例,即super内部的this指的是B的实例,因此super()在这里相当于Panent.prototype.constructor.call(this)。

作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错。

第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

  1. class Panent {
  2. method() {
  3. return 'Aliang';
  4. }
  5. }
  6. class Son extends Panent {
  7. constructor() {
  8. super();
  9. console.log(super.method()); // 'Aliang'
  10. }
  11. }
  12. let son = new Son();

上面代码中,子类Son当中的 super.p(),就是将super当作一个对象使用。这时,super 在普通方法之中,指向 Panent.prototype,所以super.p()就相当于 Panent.prototype.p()。

这里需要注意,由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。

只有定义在原型对象上,super 就可以取到。

  1. class Panent {
  2. constructor() {
  3. this.x = 'This is my Panent'
  4. Panent.prototype.x1 = 'This is my Panent'
  5. }
  6. method() {
  7. return 'Alinag'
  8. }
  9. }
  10. class Child extends Panent {
  11. constructor() {
  12. super();
  13. console.log(super.x) //undefined
  14. console.log(super.method()) // 'Alinag'
  15. console.log(super.x1) //This is my Panent
  16. }
  17. }
  18. const m = new Child()

ES6 规定,在子类普通方法中通过 super 调用父类的方法时,方法内部的this指向当前的子类实例。

如果super作为对象,用在静态方法之中,这时super将指向父类,而不是父类的原型对象。

  1. class Parent {
  2. static myMethod(msg) {
  3. console.log('static', msg);
  4. }
  5. //普通方法
  6. myMethod(msg) {
  7. console.log('instance', msg);
  8. }
  9. }
  10. class Child extends Parent {
  11. static myMethod(msg) {
  12. super.myMethod(msg);
  13. }
  14. myMethod(msg) {
  15. super.myMethod(msg);
  16. }
  17. }
  18. const child = new Child();
  19. Child.myMethod(1); // static 1
  20. child.myMethod(2); // instance 2

上面代码中,super在静态方法之中指向父类,在普通方法之中指向父类的原型对象。

另外,在子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例。

  1. class A {}
  2. class B extends A {
  3. constructor() {
  4. super();
  5. console.log(super); // 报错
  6. }
  7. }

上面代码中,console.log(super) 当中的super,无法看出是作为函数使用,还是作为对象使用,所以 JavaScript 引擎解析代码的时候就会报错。这时,如果能清晰地表明 super 的数据类型,就不会报错。

  1. class A {}
  2. class B extends A {
  3. constructor() {
  4. super();
  5. console.log(super.valueOf() instanceof B); // true
  6. }
  7. }
  8. let b = new B();

上面代码中,super.valueOf() 表明super是一个对象,因此就不会报错。同时,由于super使得this指向B 的实例,所以super.valueOf() 返回的是一个B的实例。