从零开始的JavaScript学习之旅——Week 4

188 阅读17分钟

ES5

ES5:提供了一些新的API

保护对象

保护对象:保护对象的属性和方法

四大特性可以保护对象:

  1. 对象的每一个属性都要4大特性,3个开关默认为true
举例:
    {
        value: 1001,//保存实际值的地方
        writable: true,//开关:控制属性是否可以被修改 
        enumerable: true,//开关:控制属性是否可以被for in循环遍历
        configurable: false//开关:控制属性是否可以被删除,总开关,一旦设置为false就不会允许修改其他特性,它本身一旦改为false不可逆
    }
  1. 修改对象的某个属性的四大特性:
语法:
    Object.defineProperties(obj,{
        "属性名":{四大特性},
        ...
    })

三个级别:

  1. 防扩展:防添加 语法Object.preventExtensions(obj);

  2. 密封:防添加防删除 语法Object.seal(obj);

  3. 冻结:防添加防删除防修改 语法Object.freeze(obj);


新增数组API

数组的新的API:6个

判断

判断:判断数组中的元素是否满足条件

  1. every:判断数组中的元素是否全部满足我们的条件,类似于&&,只要有一个为false则为false
  2. some:判断数组中的元素是否【包含】满足我们的条件,类似于我们的||,只要有一个为true则为true
语法:
    arr.every/some(function(val,i,arr){
        //val:当前值
        //i:当前值的下标
        //arr:数组本身
        //虽然提供了3个形参,但是到底要用几个看我们自己
        //切忌:函数自带return undefined;
        return 判断条件;
    })

遍历

遍历:把数组中的每个元素取出来执行相同 或 相似的操作

  1. forEach:直接修改原数组
语法:
    arr.forEach(function(val,i,arr){
        直接写操作;
    })
  1. map:不修改原数组,直接返回新数组
语法:
    var newArr=arr.map(function(val,i,arr){
        return 直接写操作;
    })

汇总和过滤:

  1. 过滤:筛选出符合条件的元素: - 不会修改原数组
语法:
    var subArr=arr.filter(function(val,i,arr){
        return 判断条件;
    })
  1. 汇总:将所有的数组的元素进行+-*/
语法:
    var sum=arr.reduce(function(prev,val,i,arr){
        return prev+val;
    },base)

创建子对象

根据一个父对象创建一个子对象并且继承已经设置完毕,提前保护对象

语法:
    var 子对象=Object.create(父对象,{
        "属性名":{四大特性},
        ...
    })

call/apply/bind

call/apply/bind:替换了函数中的this

  1. call/apply:临时替换了函数中的this(借)

    • 差别:
      • call:要求传入的实参必须单独传入
      • apply:要求传入的实参必须整理为一个数组,只能传一个数组参数
    • 强调:call/apply相当于立刻调用函数
    • 用法:
      • 方法名.call(借用的对象,实参,...)
      • 方法名.apply(借用的对象,[实参,...])(apply自动打散数组)
    • 固定套路:
      • Object.prototype.toString.call/apply(数组)
      • Math.max/min.apply(Math,arr);
      • 关键点:将类数组转为普通数组:lis=Array.prototype.slice.call(lis);
  2. bind:永久替换了函数中的this(买)

    • 注意:

      • 创建了一个函数功能和原函数完全一样
      • 将新函数的this永久绑定为了你指向的对象
      • 将新函数中的部分固定参数提前永久绑定
    • 用法:

      • var 新方法名=方法名.bind(永久绑定的对象,永久绑定的实参,...);
    • 强调:新方法不会立刻执行,需要手动调用

  3. 使用场景

    • 如果你的这方法要经常反复使用,可以使用bind - 买
    • 如果你需要一个方法立刻就要执行,建议call/apply - 借

严格模式

  1. 开启严格模式"use strict";(可以出现在任何作用域的顶部)
  2. 用处
    • 禁止给未声明的变量进行赋值
    • 将静默失败升级为错误

ES6

ES6:简化了(改变了)语法

模板字符串

模板字符串:支持直接在字符串中书写变量 我的名字叫${name},可以代替""和''

举例:
    var a=3,b=5;
    console.log(`${a}+${b}的结果为${a+b}`);

块级作用域

块级作用域:将var替换为let用于创建变量:优先使用let

作用

  1. let之前不允许出现未声明的同名变量(解决声明提前)
  2. 添加了块级作用域(一个{}就是一个块)
  3. 绑定事件时,会记录着当前元素的下标(不需要自己定义自定义下标)

箭头函数

箭头函数:简化一切的回调函数

回调函数:匿名函数,没有自调,就是回调

公式:去掉function(){}之间添加=>,如果形参只有一个省略(),如果函数体只有一句话省略{},如果函数体只有一句话并且是return{}return都省略

注意

  1. 箭头函数中如果出现this->外部的对象

  2. 建议在事件中暂时不要简化为箭头函数

for...of循环

语法:
    for(var v of arr){
        v;//当前值
    }

缺点

  1. 不能直接修改原数组,只能返回新数组
  2. 不能遍历hash数组,不能遍历对象(其实可以需要多加一句话)

DOM

DOM概念:Document Object Model,提供了专门用于操作HTML文档的API

DHTML

DHTML:动态的HTML,一切实现网页动态效果的技术的统称,并不是什么新技术、新概念,仅仅只是一个统称,HTML+CSS+JS(DOM)

面试/鄙视题:HTML/XHTML/DHTML/XML分别是什么:
	HTML:网页
	XHTML:更严格的网页
	DHTML:动态的网页
	XML:数据格式

DHTML模型

  1. DOM:Document Object Model(W3C标准)
  2. BOM:Browser Object Model,提供专门用于操作浏览器的API(没有标准,但是大部分浏览器厂商已经统一的实现了,除了老IE,具有大量的兼容性问题,使用的较少(重点:定时器、event事件))

DOM

DOM:可以操作一切结构化文档(HTML/XML):3部分

  1. 核心DOM:万能!但是API比较繁琐:elem.setAttribute("属性名","属性值")
  2. HTML DOM:只能操作HTML文档,API非常的简单:elem.属性名=值;
  3. XML DOM:XML基本已经淘汰很多年了 建议:以后优先使用HTML DOM,HTML DOM实现不了,在用核心DOM补充

DOM树

DOM树:保存所有网页内容的树状结构

树根document,不需要创建,由浏览器的js解释器自动创建,一个页面只有一个树根

DOM节点/对象/元素:一个标签、文本、属性、注释等等

每个DOM节点/对象/元素都有三大属性:

  1. xx.nodeType:获取xx的节点类型

    • document:9
    • 元素标签:1
    • 文本节点:3
    • 属性节点:2
  2. 属性节点.nodeValue:获取属性节点的节点值

    • 现在没用:现在用getAttribute("")能直接获取到属性的值
  3. xx.nodeName:获取属性节点的名称:

    • 找到多个子元素,但是我们希望对不同的子元素执行不同的操作
    • 特殊:获取出来的标签都是全大写

通过节点之间关系获取元素

  1. 父:elem.parentNode;
  2. 子:elem.children;(集合:只能找到儿子级)
  3. 第一个儿子:elem.firstElementChild;
  4. 最后一个儿子:elem.lastElementChild;
  5. 前一个兄弟:elem.previousElementSibling;
  6. 后一个兄弟:elem.nextElementSibling;

递归

递归:函数中,再一次调用函数自己,最后一定要结束 使用场景:用于【遍历层级不明确的树状结构】 使用步骤:2步

  1. 创建函数,传入实参树根,形参接住,直接做第一层要做的操作
语法:
    function f1(root){
        //直接做第一层要做的操作
        //判断自己有没有下一级,如果有再次调用此方法,但传入的实参已经变成了你的下一级
    }
  1. 调用函数 语法f1(实际的根元素)

算法:深度优先!优先遍历当前节点的子节点,子节点遍历完,才会跳到兄弟节点

递归、循环比较

  • 递归:
    • 优点:直观,易用
    • 缺点:效率较低,同时开启的函数很多,占用内存空间,不是任何时候都要使用 - 几乎不担心(【遍历层级不明确的树状结构】)
  • 循环:
    • 优点:效率较高
    • 缺点:难度极大!

遍历API

遍历API:专门用于遍历层级不明确的树状结构 使用步骤:2步

  1. 创建treewalker对象
语法:
    var tw=document.createTreeWalker(root,NodeFilter.SHOW_ALL/SHOW_ELEMENT);
    //SHOW_ALL查找全部,SHOW_ELEMENT查找元素
  1. 反复调用nextNode方法
语法:
    while((node=tw.nextNode())!=null){
        node;//当前节点做什么操作
    }

算法:深度优先算法

注意:此方法必须跳过起点

纯循环遍历层级不明确的树状结构

举例:
html:
    <ul id="ul">
        <li>电影</li>
        <li>动漫
            <ul>
                <li>剑风传奇</li>
                <li>海绵宝宝</li>
                <li>哆啦A梦</li>
            </ul>
        </li>
        <li>剧集
            <ul>
                <li>剑风传奇</li>
                <li>海绵宝宝</li>
                <li>哆啦A梦</li>
            </ul>
        </li>
    </ul>
 
 js:
function getChildes(root){
    var org=root;//org=ul
    while(true){
        root.style.border="1px solid #000";
        if(root.firstElementChild){
            root=root.firstElementChild
        }else if(root.nextElementSibling){
            root=root.nextElementSibling
        }else{//没有儿子,也没有下一个兄弟
            while(!root.parentNode.nextElementSibling&&root!=org.lastElementChild){
                root=root.parentNode;
            }
        root=root.parentNode.nextElementSibling;
            if(root==org.lastElementChild){
                root.style.border="1px solid #000";
                break;
            }
        }
    }
}
getChildes(ul);

总结:以后不用遍历API(只能遍历页面元素),也不用纯循环(难度大),遇到层级不明确的时候,使用递归(不仅遍历元素,还能遍历数据)

查找元素

  1. 按照HTML的特点去查找元素:4个 语法
  • var elem=document.getElementById("id");(找到了是单个元素,没找到一定是null)
  • var elem=document.getElementsByTagName/ClassName/Name("标签/class名/name值");(找到了是个集合,没找到一定是一个空集合[])
  1. 按照CSS选择器进行查找:2个
  • 单个元素,没找到一定是null,如果有多个,也只会找到第一个
    • 语法var elem=document.querySelector("任意css选择器");
  • 多个元素:找到的是一个集合,没找到空集合
    • 语法var elem=document.querySelectorAll("任意css选择器");
面试题:getXXX和querySelectXXX的区别?
        1、返回结果不同:
            getXXX - 返回是一个动态集合(每次DOM树修改,都会悄悄的再次查找)
            querySelectXXX - 返回是一个静态集合 (每次DOM树修改,不会再次查找,只管第一次找到的结果)

        2、动态集合,不支持forEach
           静态集合,支持forEach

        3、复杂查找时,尽量使用var elem=document.querySelectorAll("任意css选择器")

操作元素

操作内容

  1. innerHTML:支持标签,并且没有兼容性问题

    • 获取:elem.innerHTML;
    • 设置:elem.innerHTML="新值";
  2. textContent:不支持标签,并且有兼容性问题(老IE:innerText,老IE不支持textContent,主流浏览器都支持innerText)

    • 获取:elem.innerText;
    • 设置:elem.innerText="新值";
  3. value:专为input、select准备的

    • 获取:elem.value;
    • 设置:elem.value="新值";

操作属性

  1. 获取属性值:

    • 核心DOM:万能的,可以操作一切属性

      • 语法elem.getAttribute("属性名");
    • HTML DOM:只能操作标准属性,并且class需要写出className

      • 语法elem.属性名;
  2. 设置属性值:

    • 核心DOM:万能的,可以操作一切属性

      • 语法elem.setAttribute("属性名","值");
    • HTML DOM:只能操作标准属性,并且class需要写出className

      • 语法elem.属性名="新值";
  3. 删除属性:

    • 核心DOM:删干净整个属性节点
      • 语法elem.removeAttribute("属性名");
    • HTML DOM:赋值为空,删不干净,属性值确实没了,但属性名还在,有的属性只需要属性名也具有效果(href、disabled、readonly)
      • 语法elem.属性名="";
  4. 判断属性:

    • 核心DOM:只能判断有没有
      • 语法elem.hasAttribute("属性名");
    • 推荐:使用getAttribute
      • 语法if(a1.getAttribute("属性名")=="属性值"){console.log("有并且是");}

操作样式

  1. 内联:优先级最高,只会操作某个元素,不会牵一发动全身

    • 获取:elem.style.css属性名;
    • 设置:elem.style.css属性名="css属性值"; css属性名如果有横线,去掉横线写为小驼峰命名法
  2. 样式表:4步

语法:
    //获取哪一个样式表
    var sheet=document.styleSheets[1];
    //获取所有的样式规则
    var rules=sheet.cssRules;
    //获取到了我想要操作的样式规则
    var rule=rules[36];
    //修改或获取样式
    console.log(rule.style.background);
    rule.style.background="purple";

创建元素&渲染页面&删除元素

创建元素&渲染页面

创建元素:3步

  1. var 空标签=document.createElement("标签名");

  2. 设置必要的属性或事件

    • 空标签.属性名="值"
    • 空标签.on事件名=function(){//操作}
  3. 渲染页面元素:3种

    • 父元素.appendChild(新元素);//新元素会插入到父元素里面的末尾
    • 父元素.insertBefore(新元素,已有子元素);//新元素会插入到父元素里面的已有子元素之前 - 不推荐:修改其他的人下标
    • 父元素.replaceChild(新元素,已有子元素);//新元素会替换掉父元素里面的已有子元素
  4. 删除元素:

    • 元素.remove();

HTML DOM常用对象

HTML DOM提供了一些常用对象:简化了核心DOM的操作,但是不是所有都能简化

  1. image:简化了创建

    • 语法var img=new Image();
    • 强调:在DOM中,不是所有元素都具有构造函数创建方式
  2. form:简化了查找

    • 查找form元素:var form=document.forms[i];
    • 查找表单控件:var input=form.elements[i];
    • 专属事件:onsubmit事件:提交的一瞬间会执行,也可以阻止提交return false;
  3. select

    • 属性:2个

      • options === children:获取到select下面的所有的option
      • selectedIndex - 获取到当前选中项的下标,只要是做联动,必不可少
    • 方法:

      • select.add(option);,完全等效于appendChild,追加元素
      • select.remove(i);,删除select中的第i个option
      • 专属事件:onchange:选中项发生改变时触发
  4. option:简化了创建

    • 语法var opt=new Option("innerHTML","value");

BOM

BOM:Browser Object Model(浏览器 对象 模型),提供了专门用于操作浏览器的API(没有标准,使用的较少,但大部分浏览器厂商已经统一实现了(除了老IE))

BOM对象

BOM对象:window(定时器)、history、location、navigator、event、screen...

window对象

window对象:扮演了2个角色:

  1. 浏览器中,window代替了ES的Global充当全局作用域,包含了所有的全局对象、变量、函数

  2. 指代当前浏览器的窗口

网页打开新链接的方式(提升用户的体验感):

  1. 替换当前页面,可以后退:

    HTML<a href="url">文字</a>

    JSopen("url","_self");

  2. 替换当前页面,禁止后退:

    使用场景:电商网页:结账完毕后,不允许用户后退

    history对象:保存了当前窗口的历史记录,功能:前进后退

    location:保存了当前窗口正在打开的url

    语法location.replace("新url");,替换当前网址、不会产生历史记录

  3. 在新窗口打开,可以打开多个:

    HTML<a href="url" target="_blank">文字</a>

    JSopen("url","_blank");

  4. 在新窗口打开,只能打开一个:

    使用场景:电商网页:跳转到 支付结账页面,只允许用户打开一个

    HTML<a href="url" target="自定义">文字</a>

    JSopen("url","自定义");

    自定义:窗口的底层都有一个名字,如果出现重复的名字则新窗口会替换掉原来的旧窗口

扩展:a标签可以做的事?

  1. 跳转
  2. 锚点
  3. 下载:<a href="xx.rar/zip/exe">文字</a>
  4. 打开图片、txt:<a href="xx.图片后缀/txt">文字</a>
  5. 直接书写js:<a href="javascript:js代码;">文字</a>

window对象的属性和方法

  • 属性:

    • 获取浏览器窗口的大小:
      • 文档显示区域的大小:body部分

        innerWidth/innerHeight

      • 完整的浏览器大小:

        outerWidth/outerHeight

      • 屏幕的大小:桌面应用才用的

        screen.width/height

  • 方法:

  1. 打开新窗口:

    • 语法var newW=open("url","自定义name","width=,height=,left=,top=");

    • 特殊

      1. 如果没传入第三个参数,新窗口会和浏览器并为一体

      2. 如果传入第三个参数,新窗口会脱离浏览器

      3. 建议宽高不小于200

      4. 有可能会被浏览器拦截

  2. 关闭窗口:

    • 语法窗口.close();
  3. 修改窗口的大小:

    • 语法新窗口.resizeTo(newW,newH);
  4. 修改窗口的位置:

    • 语法新窗口.moveTo(x,y);
  5. 定时器:2种

    • 周期性定时器:先等待,在执行一次,在等待,在执行一次...

      • 开启:timer=setInterval(callback,间隔毫秒数);
      • 停止:clearInterval(timer);
    • 一次性定时器:先等待,在执行一次,结束

      • 开启:timer=setTimeout(callback,间隔毫秒数);
      • 停止:clearTimeout(timer);
    • 注意:两种定时器底层相同,可以互换

扩展:获取鼠标的坐标:

  1. 事件中传入一个形参e->获得到事件对象event(其中一个作用获取鼠标的坐标)
  2. 获取:
    e.screenX/Y;//相对于屏幕
    e.clientX/Y;//相对于浏览器客户端
    e.pageX/Y;//相对于网页的
举例:
    鼠标跟随效果:关键点:
        1、绑定事件:window.onmousemove
        2、图片位置一定和鼠标位置一起
        3、图片的加载速度比js的执行速度要慢,
            img.onload=function(){
                //最后才会执行
            }

history

history:保存当前窗口的历史记录(历史url) 作用

  1. 前进:history.go(1);
  2. 后退:history.go(-1);
  3. 刷新:history.go(0);

location

location:保存当前窗口的正在打开的url

  1. 常识:一个url由5部分组成
举例:
    http://127.0.0.1:8020/bom02/new/01-3.html
    https://www.baidu.com/s?wd=178&rsv_spt=1&rsv_iqid=0xd702eab1000426fa&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&rqlang=cn&tn=baiduhome_pg&rsv_enter=1&rsv_dl=tb&oq=178%2526lt%253B&rsv_btype=t&inputT=204&rsv_t=8b56iJOG%2B6b45FILzCXex%2FpEHFfVJ6HMrt%2FXQdYMKdOy%2B8bu9OZqejCb14LrHOIywVaU&rsv_sug3=7&rsv_sug1=4&rsv_sug7=100&rsv_pq=a3ee42be00024fae&rsv_sug2=0&rsv_sug4=981
    
    1. 协议:http/https(请求响应模式)/ftp(传输文件)/ws(广播收听模式:直播) - 网页的功能不同
    2. 主机号/域名:www.baidu.com/127.0.0.1 - 主机号不方便记忆所以多半都会购买域名,而且127.0.0.1只能访问自己
    3. 端口号::8020 - 默认端口:http - 80 https - 443,默认端口可以省略不写
    4. 文件的相对路径:new/01-3.html  /s -百度加密了
    5. 查询字符串:?wd=178&rsv_spt=1... - 表单提交到服务器的东西 - 请求消息
  1. 属性:不需要记忆,直接用console.log(location)可以查看到有哪些属性

    • 作用:获取:协议、域名、端口号、文件路径、查询字符串
  2. API:跳转

    • location="新url"
    • location.href="新url"
    • location.assign("新url");
    • 替换后禁止后退:location.replace("新url")
    • 刷新:location.reload();

navigator

navigator:保存了当前浏览器的信息(浏览器的名称/版本号)

  • 属性:navigator.userAgent;
  • 作用:根据字符串截取出浏览器名称&版本号:本来的目的是为了做兼容,但是没用因为所有的API前辈们都已经设置好了兼容

event

event:事件对象

  • 事件:多半用户触发或浏览器自动触发的操作
  1. 绑定事件:3种

    • 在HTML元素上绑定

      • 语法:<elem on事件名="js语句"></elem>
      • 缺点:
        1. 没有实现JS与HTML的分离 - 要HTML(内容)/CSS(样式)/JS(行为)分离
        2. 无法动态绑定事件 - 一次只能绑定一个元素
        3. 无法同时绑定多个函数对象
    • 使用JS的事件属性绑定:

      • 语法:elem.on事件名=function(){js语句}
      • 优点:
        1. 实现JS与HTML的分离
        2. 动态绑定事件
      • 缺点:无法同时绑定多个函数对象
    • 使用JS的API绑定事件:

      • 主流:elem.addEventListener("事件名",callback)

      • 老IE:elem.attachEvent("on事件名",callback);

      • 兼容:if(elem.addEventListener){ elem.addEventListener("事件名",callback) }else{ elem.attachEvent("on事件名",callback); }

      • 优点:

        1. 实现JS与HTML的分离
        2. 动态绑定事件
        3. 同时绑定多个函数对象
      • 缺点:需要处理兼容性问题

  2. 事件周期:

    • 主流:3个
      1. 捕获阶段:由外向内,记录着要执行的事件
      2. 目标触发:当前点击的元素优先出发
      3. 冒泡触发:由内向外的冒泡触发事件
    • 老IE:2个
      1. 目标触发:当前点击的元素优先出发
      2. 冒泡触发:由内向外的冒泡触发事件
  3. 获取到事件对象event:

    • 主流:事件函数中传入一个形参e,就可以自动接住事件对象event

    • 老IE:window.event;

    • 兼容:event;,不光老IE可用,主流浏览器也可以使用

    • 获取到了事件对象event就可以做操作了:

      • 获取鼠标的位置

      • 阻止冒泡:重要在于面试中/鄙视中,但是开发中绝对不会阻止冒泡,更需要利用冒泡

        1. 主流:e.stopPropagation();
        2. 老IE:e.cancelBubble=true;
        3. 兼容:if(e.stopPropagation){e.stopPropagation()}else{e.cancelBubble=true;}
      • 事件委托(利用冒泡):

        1. 为什么:优化,每绑定一个事件,相当于就创建了一个事件监听对象,创建的对象越多,网页的性能越底下

        2. 建议:建议把事件都绑定在自己的父元素身上一次即可

        3. 获取目标元素:

          触发的元素 - > 永远不变就是你触发到的元素

          当前元素:this -> 指向很多

          语法:
              主流:e.target;
              老IE:e.srcElement;
              兼容:e.srcElement;不光老IE可用,主流浏览器也可以使用
          
      • 阻止浏览器的默认行为:

        1. 默认行为:比如:F5带来的刷新,F12带来的控制台,右键带来的菜单框,F11全屏,a自带跳转,submit自带提交...

          语法:
              主流:e.preventDefault();
              老IE:e.returnValue=false;
              兼容:if(e.preventDefault){
                      e.preventDefault()
                    }else{
                      e.returnValue=false;
                    }
          
        2. 新事件:

          鼠标右击:window.oncontextmenu

          键盘事件:window.onkeydown

      • 获取键盘的键码

        语法: e.keyCode;

扩展:客户端存储技术 - 以前使用cookie(缺点:大小有限2kb,30天必死),HTML5提出了一个新概念webStorage:2种(优:大小有限8mb,生命周期无限,缺点:老IE不支持) 1. localStorage:本地级 - 浏览器关闭甚至电脑关闭,第二天打开依然存在 2. sessionStorage:会话级 - 浏览器一旦关闭自动死亡

  • 作用:

    1. 数据可以跨页面传输
    2. 皮肤、主题
    3. 浏览器的历史记录
  • 语法:非常简单,类似于对象:

    1. 添加:xxxxStorage.属性名="值";
    2. 获取:xxxxStorage.属性名;
    3. 删除:xxxxStorage.removeItem("属性名");,只会删除这一个
    4. 清空:xxxxStorage.clear();,删除所有