JS知识点总结(4)

74 阅读16分钟

22.面向对象开发

三大特点:封装、继承、多态

==面试题==:简单的说一下你了解的面向过程和面向对象 开发方式的区别? 面向过程:开始->经过->结束,我们从开始到现在学习写法的一直都是面向过程 一个人:出生->成长->工作->结婚->死亡 面向对象:对象(属性和方法),如果这个世界有各种属性和各种方法,但是连一个对象/生物都没有,那这些东西就没有具体的意义 我们把所有的代码全部包含在一个对象中来进行描写,才更符合现实生活 一个人:属性:身高、体重、姓名、性别 方法:吃饭、睡觉、玩耍

封装/定义/创建对象的三种方式

  1. 直接量方式: var obj={ "属性名":属性值, ... "方法名":function(形参){函数体}, ... };
  • 强调:
    1. 属性名和方法名的引号其实是可以省略的,但是不建议,因为以后我们会学习一种数据格式叫做JSON,要求属性名和方法名的【双引号】不能省略
    2. 如何访问对象的属性和方法
      • *对象名.属性名; === 对象名["属性名"]
      • *对象名.方法名(); === 对象名"方法名" JS中万物皆对象,除了undefined和null,一切对象的底层都是hash数组
      • 特殊:
        1. 访问到不存在的属性,返回undefined
        2. 可以随时随地添加不存在的属性和方法
    3. 希望获取到对象之中所有的东西:遍历对象:for in循环 for(var i in obj){ obj[i]; }
    4. 如果你希望在对象的方法内部使用对象自己的属性,我们需要写为this.属性名; this指向:
      • 单个元素绑定事件this->这个元素
      • 多个元素绑定事件this->当前元素
      • 函数中this->谁在调用此函数this指向就是谁
      • 定时器中this->window
      • 箭头函数this->外部对象
      • 自定义构造函数中出现this->你当前正在创建的对象
  1. 预定义构造函数方式:垃圾 var obj=new Object();//空对象 obj.属性名=属性值; obj.方法名=function(){};

以上两种创建方式有一个缺点:仅仅适合创建单个对象,如果创建多个对象,代码冗余度太高

  1. 自定义构造函数方式:(专门为创建多个对象准备的)
    1. 创建构造函数 function 类名(name,age,hobby,...){ this.name=name; this.age=age; this.salary=salary; ... }
    2. 调用自定义构造函数创建出对象 var obj=new 类名(实参,...);

面向对象开发方式的好处: 1、特地把每一块功能分开写 - 哪怕不需要注释,也便于以后维护! 2、铁索连舟 - 更符合现实生活 3、所有东西都包含在一个对象之中 - 更符合现实生活

  • 继承:父对象属性和方法,子对象可以直接使用 原因:代码复用,节约内存空间 使用场景:只要多个子对象公用的属性和【方法】,都要集中定义在父对象之中
  • JS的面向对象是基于原型的
    • 原型的定义:保存一类子对象共有属性和共有【方法】的父对象(原型对象),每个对象天生就有一个原型
  • 获取原型对象:2种
    1. 对象名.proto; - 前提:必须先创建出一个对象,才可以使用此属性找到原型对象
    2. 构造函数名.prototype; - 构造函数名:Array、Function、Date、RegExp、h52204... - 哪怕没有创建过任何对象也能找到原型
  • *****两链一包:
  • 作用域链:以函数EC的scope chain属性为起点,经过AO逐级引用,形成的一条链式结构,作用:查找变量的,带来了变量的使用规则:优先使用自己的,自己没有找全局,全局没有则报错
  • 闭包:保护了一个可以反复使用的局部变量的词法结构
  • 原型链:每个对象都有一个.__proto__的属性,可以不断的连续.找到爸爸-爷爷-祖祖...形成的一条链式结构 经过尝试,发现最顶层:Object.prototype是对象的原型,所有也就有了一句话:万物皆对象 作用:查找属性和方法,自己没有的属性和方法,可以自动顺着原型链进行查找,所以我们知道为什么人人都可以使用toString,因为toString在最顶层的
  • 获取到原型对象则可以设置共有属性和共有方法
    • 原型对象.属性名=属性值;//共有属性
    • 原型对象.方法名=function(){};//共有方法
  • 自有和共有
    • 自有:保存在对象本地的属性
    • 共有:保存在父(原型)对象的属性,所有的子对象都可以使用 ==笔试题==
  1. 如何判断自有和共有
  • 判断自有:obj.hasOwnProperty("属性名");//返回一个布尔值:true说明是自有,false可能是共有也可能是没有
  • 判断共有:2个条件:
    1. 不是自有:obj.hasOwnProperty("属性名")==false;
    2. 自动在原型链检查:"属性名" in 对象名; if(obj.hasOwnProperty("属性名")==false&&"属性名" in 对象名){//共有}
   if(obj.hasOwnProperty("属性名")){
     console.log("自有");
   }else{
     if("属性名" in 对象名){
       console.log("共有")
     }else{
       console.log("没有")
     }
   }
  1. 修改或删除属性

    • 修改和删除自有属性

      • 修改:obj.属性名=新值;
      • 删除:delete obj.属性名;
    • 修改和删除共有属性

      • 修改:原型对象.属性名=新值;
      • 删除:delete 原型对象.属性名;
  2. 为老IE的数组添加indexOf方法(这道题不是固定的:为某一类对象设置一个方法)

if(Array.prototype.indexOf===undefined){//判断浏览器有没有这个方法,没有才添加此方法
 Array.prototype.indexOf=function(key,starti){//indexOf的执行原理
   starti===undefined&&(starti=0);//说明用户没有传入开始位置,我们就给用户设置为从下标0开始查找
   for(var i=starti;i<this.length;i++){//从开始位置处,循环数组后面的每一个文字和用户输入的关键字进行匹配
     if(this[i]==key){
       return i;//匹配到了返回对应的下标
     }
   }
   //没匹配到,返回-1
   return -1;
 }
}
  1. **判断x是不是数组?**4种方法
    1. 判断x是不是继承自Array.prototype
      • Array.prototype.isPrototypeOf(x);
      • 如果返回true,说明是数组,否则不是数组
    2. 判断x是否是由构造函数Array创建的
      • x instanceof Array
    3. ES5提供了一个API:Array.isArray(x);(ES5以上的内容,老IE都不支持)
    4. 输出对象的字符串形式:
      • 在Object的prototype原型上放着最原始的toString
      • 原始的toString,默认输出[object 构造函数名] 多态(override)/重写:子对象绝对父对象的成员不好用,可以在本地定义同名成员,覆盖父对象之中的成员 我希望借用到函数:跳过爸爸,直接去找爷爷拿toString: Object.prototype.toString.apply(x)==="[object Array]";
  2. 实现自定义继承:
    1. 实现两个对象之间的继承
      • 子对象.__proto__=父对象;
    2. 直接匹配设置继承
      • 构造函数名.prototype=父对象;
      • 时机:先设置好父对象,再创建子对象

23.ES5

  1. 保护对象:保护对象的属性和方法(不重要)

    • 四大特性:每个属性都有四大特性
      • "value": 3500, //实际保存属性值的地方
      • "writable": true,//开关:控制着是否可以被修改
      • "enumerable": true,//开关:控制着是否可以被for in循环遍历到
      • "configurable": true//开关:控制着是否可以被删除
    • 修改四大特性:
      • Object.defineProperties(obj,{"属性名":{四大特性}})
    • 三个级别:
      1. 防扩展:禁止给对象添加任何新属性
        • Object.preventExtensions(obj);
      2. 密封:禁止给对象添加任何新属性,也不能删除属性
        • Object.seal(obj);
      3. 冻结:禁止给对象添加任何新属性,也不能删除属性,也不能修改属性
        • Object.freeze(obj);
  2. 数组的新的API(3组6个,仅数组可用!)

    1. 判断:判断数组中的元素是否符合要求
      • every - 每一个:判断数组中的每个元素【都】要符合要求最后结果才为true,类似于&&,只要有一个不满足,结果则为false
        • 语法:var bool=arr.every(function(val,i,arr){return 判断条件;})
      • some - 有一些:判断数组中的是否【包含】符合要求的元素,只要有一个最后结果则为true,类似||,只要有一个满足,则为true,只有全都不满足,结果才为false
        • 语法:var bool=arr.some(function(val,i,arr){return 判断条件;})
      • 回调函数中的val为当前元素,i为当前元素的下标,arr为数组本身;
    2. 遍历:对每个元素执行相同或相似的操作
      • forEach:直接修改原数组
        • 语法:arr.forEach(function(val,i,arr){操作;})
      • map:不修改原数组,返回新数组
        • 语法:var newArr=arr.map(function(val,i,arr){return 操作;})
    3. 过滤和汇总:
      • 过滤:筛选出原数组之中符合条件的元素组成一个新数组!原数组不变!
        • 语法:var newArr=arr.filter(function(val,i,arr){return 判断条件;})
      • 汇总:将数组中每一个元素,取出来整合为一个最终结果
        • 语法:var sum=arr.reduce(function(prev,val,i,arr){return prev+val;},基础值)

以上6个API其实都是简化了同一件事即for循环

  1. Object.create()方法:直接用父对象创建子对象,并且子对象扩展自有属性

    • 语法:var 子对象=Object.create(父对象,{"自有属性名":{四大特性},...})
  2. 严格模式:很严格

    • 语法:"use strict",可以放在任何一个作用域的顶部
    • 效果:
      • 禁止给未声明的变量赋值 - 解决了全局污染
      • 将静默失败升级为了错误
    • 注:gulp和webpack中,可能打包时就会自动给生成一个"use strict",需要删掉
  3. call、apply、bind:不是自己的方法,也可以用到

    • call、apply:【临时替换函数中的this】,借用
      • 差别:
        • call,要求传入函数的实参必须单独参入
        • apply,要求传入函数的实参必须是一个数组
      • 语法:
        • 要借用的函数.call(借用的对象,实参1,...);
        • 要借用的函数.apply(借用的对象,arr); - apply除了有借用的功能,还会悄悄的打散我们的数组
    • bind:【永久替换函数中的this】,买
      • 语法:var 新函数=老函数.bind(指定的对象)
      • 效果:
        1. 创建了一个和原函数功能完全一样的新函数
        2. 将新函数中的this永久绑定为你指定的对象
        3. 将新函数中的部分参数永久固定
      • 注意:bind绑定在新函数中的this,无法被call、apply再次替换借走

总结:如果临时调用一个函数,立刻执行时 -- call/apply 如果创建一个函数提前绑定this,不一定希望立刻执行 - bind 使用场景:

  1. 比较出数组中的最大值和最小值:Math.max/min.apply(Math,arr)
  2. 得到Object最原始的toString:Object.prototype.toString.call/apply(arr);
  3. 将类数组对象转为普通数组:var 新数组=Array.prototype.slice.call/apply(类数组对象)
    • 其实ES5还提供了一个数组API,可以直接将类数组对象转为普通数组:var 新数组=Array.from(类数组对象)

24.ES6

  1. 常量也是ES6提供的
  2. 模板字符串:简化字符串拼接,支持在模板字符串之中书写变量
    • 语法:`字符${变量名}`
    • 原理:在字符串中实现了一个简单的js环境
  3. let关键字:创建变量以后优先使用let,再考虑var
    • 语法:let 变量名=值;
    • 优点
      • 解决了声明提前
      • 添加了块级作用域,一个{}就是一个块
      • 如果绑定事件时,用到了let来遍历,那么let会记录住你当前元素的下标 - 你以后再也不需要自定义下标了
  4. 箭头函数:简化一切的回调函数
    • 语法:去掉function,()和{}之间添加=>,
    • 省略写法:
      • 形参只有一个可以省略()
      • 函数体只有一句话,省略{}
      • 函数体只有一句话,并且是return,省略{}和return
  5. for...of循环(不好用)
    • 语法:for(var v of arr){循环执行语句}//其中v为当此循环arr的值
    • 缺陷:
      • 不能修改原数组
      • 不能遍历hash数组和对象
  6. *解构赋值
  7. Set和Map类型
  8. *模块化开发
  9. *Promise
  10. *Class

25.地图

科普:定位技术? 1、GPS - 美国(安全性、商机、专利费) 2、北斗 - 中国(军用:打仗:惯性定位:不准确) (民用:卫星定位:精准) (车联网、物联网) 3、IP定位 - 公用IP,需要去联通、电信、铁通。。。办理 4、基站定位 - 不准确,半斤范围2公里

网站:IP定位 - 我们不需要自己写任何定位相关的技术,在中国提供了流行两个定位地图(百度、高德) 固定步骤: 1、打开百度,搜索百度地图开放平台 2、右上角,注册/登录(百度账号) 3、拉到最下面,点击立即注册:认证成为开发者 4、应用管理-》我的应用-》创建应用 5、应用名称:随便 应用类型:浏览器端 不要取消任何东西 白名单:* 6、恭喜你:你已经拿到密钥、基本上要结束了 7、鼠标放到导航条上的开发文档-》javascript API-》示例demo-》挑选你喜欢的地图-》记得用上密钥 8、修改css、js 注意:2021年初,百度地图进行了更新,带来了普通版和WEBGL版,两个版本是不可以混搭的

26.DOM

  1. DOM概念 将每一个标签/属性/文本/注释/元素,都看作是一个DOM元素/节点/对象(提供了一些操作元素的属性/方法) ==面试题== HTML/XHTML/DHTML/XML 分别是什么? 1、HTML - 网页 2、XHTML - 更严格的HTML,HTML5->XHTML->HTML4.01 3、DHTML - 动态的网页:D:Dynamic - 其实并不是新技术、新概念,是将现有技术的整合统称,让我们在离线时,网页也具有动态效果 DHTML:html+css+js(dom) 4、XML - 未知的标记语言,一切的标记由自己定义,数据格式

DOM原本是可以操作一切结构化文档的HTML和XML,后来为了方便各类开发者分为了3部分:

  1. 核心DOM:既可以操作HTML又可以操作XML

    • 缺陷:API比较繁琐
  2. HTML DOM:只能操作HTML,API简单

    • 缺陷:比如属性部分,只能访问标准属性,不能访问自定义属性
  3. XML DOM:只能操作XML(XML已经淘汰了,现在最流行的数据格式是JSON) 开发建议:优先使用HTML DOM,HTML DOM实现不了再用核心DOM进行补充

  4. DOM树: 概念:DOM将我们的HTML看做了是一个倒挂的树状结构,但是树根不是html标签,而是document对象 document对象:不需要去创建,由浏览器的js解释器自动创建,一个页面只有一个document 作用:可以通过树根找到页面上的每一个DOM元素/节点/对象,也可以操作它

  5. 每个DOM元素都有三大属性:

    1. elem.nodeType:描述节点的类型
      • document节点:9
      • element节点:1
      • attribute节点:2
      • text节点:3
      • 注:以前用于判断xx是不是一个页面元素(children不会得到文本节点的,但是以前的childNodes会得到文本节点)
    2. attrNode.nodeValue:描述节点的值
      • 注:以前有用:先拿到属性节点,再用此属性去获取属性值
    3. ***elem.nodeName:描述节点的名字(e.target中也会用到)
      • 作用:拿到当前元素的标签名,判断xx是什么标签
      • 注:返回是一个全大写组成的标签名

查找元素

  1. 通过关系查找元素: 前提条件:必须先找到一个元素才可以调用关系网

    • 父元素:elem.parentNode;
    • 子元素:elem.children; (找到的是一个子元素集合)
    • 第一个儿子:elem.firstElementChild;
    • 最后一个儿子:elem.lastElementChild;
    • 前一个兄弟:elem.previousElementSibling;
    • 后一个兄弟:elem.nextElementSibling;
  2. 递归:简单来说就是函数中,又一次调用了函数自己,迟早有一天会停下来

    • 使用场景:遍历DOM树,专门用于【遍历层级不明确】的情况,既可以遍历层级不明确的DOM树,也可以遍历层级不明确的数据!
    • 使用方法:2步 function 函数名(root){ 1、第一层要做什么直接做! 2、判断他有没有下一级,如果有下一级,再次调用此函数,但是传入的实参是他的下一级 } 函数名(实际的根)
    • 算法:深度优先!优先遍历当前系欸但的子节点,子节点遍历完才会跳到兄弟节点
    • 缺点:同时开启大量的函数调用,大量消耗内存,只有一个情况才用:【遍历层级不明确】
    • 递归 vs 循环:
      • 递归
        • 优点:直观、易用
        • 缺点:性能较低,尽量只在层级不明确的时候使用
      • 循环
        • 优点:几乎不占用内存
        • 缺点:使用难度高
  3. TreeWalker(专门用于遍历层级不明确的DOM树的API)

    • 使用方法:2步
      1. 先创建出tw: var tw=document.createTreeWalker(根元素,NodeFilter.SHOW_ALL/SHOW_ELEMENT);
      2. tw对象过后,反复调用nextNode方法找到下一个节点,迟早有一天会等于null,说明没找到,公式: while((node=tw.nextNode())!=null){node要干什么}
    • 缺陷:
      • 自动的跳过根元素,根元素是不会做任何操作的
      • 仅仅只能遍历层级不明确的DOM树,不能遍历层级不明确的数据
  4. API直接找元素:

    • 根据HTML的特点去找元素
      1. 通过ID查找元素
        • 语法:var elem=document.getElementById("id值");
        • 特殊:
          1. 返回值,找到了返回的是一个当前找到的DOM元素;没找到,返回一个null,做了别的操作可能就会报错
          2. 找到了多个相同的id,那么只会返回第一个
          3. 一次只能获取一个元素,也只能操作一个元素
          4. 其实根本不需要使用此方法,直接写ID也可以找到元素
      2. 通过标签名查找元素
        • 语法:var elems=document/已经找到了的父元素.getElementsByTagName("标签名");
        • 特殊:
          1. 返回值:找到了返回的一个是==类数组==DOM集合(很像数组,都能用下标,都能用length,都能遍历),没找到返回一个空集合
          2. JS不能直接操作DOM集合,只能直接操作DOM元素,解决:要么使用下标拿到某一个元素,要么使用遍历拿到每一个元素
          3. 不一定非要从document开始查找,如果document去找,会找到所有的元素,可以换成我们已经找到的某个父元素,就只会找到这个父元素下面的元素了
      3. 通过class名查找元素
        • 语法:var elems=document/已经找到了的父元素.getElementsByClassName("标签名");
      4. 通过Name查找元素
        • 语法:var elems=document/已经找到了的父元素.getElementsByName("标签名");
    • 根据CSS选择器去找元素:
      • 单个元素:
        • 语法:var elem=document.querySelector("任意的css选择器");
        • 强调:万一选择器匹配到多个,只会返回第一个,没找到返回null
      • 多个元素:
        • 语法:var elem=document.querySelectorAll("任意的css选择器");
        • 强调:找到了返回集合,没找到返回空集合

==面试题/笔试题==: getXXX 和 querySelectorAll 有什么区别? 返回结果不同的: 1、getXXX:返回的是要给动态集合HTMLCollection 优点:数据始终和DOM树实时挂钩 缺点:每次DOM树进行修改,都会悄悄的再次查找元素,效率相对较低 2、querySelectorAll:返回的是要给静态集合NodeList 优点:每次不会悄悄重新查找,效率较高,而且还支持使用forEach!

总结:找元素的方式:

  1. 直接查找元素:getXXX 和 querySelectorAll
  2. 通过关系找元素:
  3. 层级不明确的情况才用递归!

操作元素

前提:找到元素才能操作元素: <标签 属性名="属性值" style="样式">内容</标签> 强调:页面上获取/设置到的一切数据都是字符串类型

  1. 元素的内容:
    1. elem.innerHTML:获取/设置开始标签到结束标签之间的内容(支持识别标签,无兼容性问题)
      • 获取:elem.innerHTML;
      • 设置:elem.innerHTML="新内容";
    2. elem.textContent:获取/设置开始标签到结束标签之间的纯文本(不支持识别标签,有兼容性问题)
      • 获取:elem.textContent;
      • 设置:elem.textContent="新文本";
      • 老IE:elem.innerText;(我们第一次见到小三上位)
        • innerText某次浏览器更新后,现在主流浏览器也支持此属性
        • 为什么老IE不将就主流?- 微软的,windows系统的使用的人多 以上两个属性都是为双标签准备的,但是操作不了单标签input的内容
    3. input.value:获取或设置表单控件的值(用于正则验证等场景)
      • 获取:input.value;
      • 设置:input.value="新值";
  2. 元素的属性:
    • 获取属性值
      • 核心DOM:elem.getAttribute("属性名");
      • HTML DOM:elem.属性名;
    • 设置属性值:
      • 核心DOM:elem.setAttribute("属性名","属性值");
      • HTML DOM:elem.属性名="属性值";
    • 删除属性值:
      • 核心DOM:elem.removeAttribute("属性名");
      • HTML DOM:elem.属性名=""
        • 删除不推荐使用HTML DOM,删除不干净属性节点,而有的属性,光有属性名就已经具有功能了
    • 判断有没有属性(没用)
      • 核心DOM:elem.hasAttribute("属性名")
        • 仅仅只能判断有没有这个属性,不能判断出属性值是什么,往往我们自己用获取属性值的操作,获取到了过后再去进行比较运算
      • HTML DOM:elem.属性名!="";
    • 缺陷:
    1. class必须写为className - 2015年过后,ES6诞生过后,class变成了一个关键字
    2. 不能操作自定义属性,只能操作标准属性
  3. 元素的样式
  • 内联样式
    • 优点:
      1. 仅仅当前元素可用,不会牵一发动全身
      2. 优先级最高,一定会覆盖其他的样式
    • 获取:elem.style.css属性名;
    • 设置:elem.style.css属性名="css属性值";
    • 特殊:
      1. css属性名,有横线的地方,去掉横线,变为小驼峰命名法 如:border-radius -> borderRadius
      2. 缺陷:获取时,只能获取内联样式
  • 样式表中的样式
    1. 找到你想要操作的样式表
      • var sheet=document.styleSheets[i];
    2. 获取样式表中所有的样式规则
      • var rules=sheet.cssRules;
    3. 找到自己想要操作的样式规则
      • var rule=rules[i];
    4. 要么获取要么设置
      • console.log(rule.style.css属性名);
      • rule.style.css属性名="css属性值";
  • 一切的获取都是为了判断;一切的设置都是修改或添加
  1. 绑定事件: elem.on事件名=function(){ 操作; } ==this==关键字:目前只能用于事件内: 如果单个元素绑定事件:this->这个元素 如果多个元素绑定事件:this->当前触发事件的元素

总结: 获取 - 往往都是用与判断比较 设置 - 就是添加/修改

创建元素并渲染DOM树

  1. 创建空标签:
    • var 新元素=document.createElement("标签名");
  2. 为此标签设置必要的属性和事件
    • 新元素.属性名="属性值";
    • 新元素.on事件名=function(){操作}
  3. 把新元素上DOM树
    • 父元素.appendChild(elem); - 将elem追加到父元素里当最后一个儿子
    • 父元素.insertBefore(elem,已有子元素) - 将elem追加到父元素里,但是会插入在已有子元素的前面,缺点:会影响到其他元素的下标
    • 父元素.replaceChild(elem,已有子元素) - 将elem追加到父元素里,但是会替换在已有子元素的

删除元素

elem.remove();

HTML DOM常用对象(简化核心DOM)

  1. image对象:仅仅只是简化了创建语句
    • 创建:var img=new Image() === var elem=document.createElement("img");
  2. form对象:简化了查找元素
    • 查找form元素:var form=document.forms[i];
    • 查找form元素中的表单控件:var inp=form.elements[i];
    • 专属事件:form.onsubmit=function(){return false;//阻止提交}//只在表单提交时触发的事件
  3. select对象:
    • 属性:
      • select.options;//得到select下面所有的option,完全等效于xx.children;
      • select.selectedIndex;//得到当前选中项的下标,当时做二级(多级)联动就用到了他
    • 方法:
      • select.add(option);//完全等效于appendChild
      • select.remove(i);//删除下标为i的option
      • 专属事件:select.onchange=function(){//只有选中项发生变化了才会触发}
  4. option对象:仅仅只是简化了创建语句,但是非常牛逼
    • 创建:var opt=new Option("innerHTML","value");
    • 一句话完成四个操作:select.add(new Option("innerHTML","value"))

27.BOM

浏览器弹出框 1、警告框:alert(); 2、用户输入框:var user=prompt("提示文字","默认值"); 3、用户输入框:var bool=confirm("提示文字");