JavaScript 基础

164 阅读14分钟

JS

JavaScript 的组成

  • ECMAScript - 语法规范
    • 变量、数据类型、类型转换、操作符
    • 流程控制语句:判断、循环语句
    • 数组、函数、作用域、预解析
    • 对象、属性、方法、简单类型和复杂类型的区别
    • 内置对象:Math、Date、Array,基本包装类型String、Number、Boolean
  • Web APIs
    • BOM
      • onload页面加载事件,window顶级对象
      • 定时器
      • location、history
    • DOM
      • 获取页面元素,注册事件
      • 属性操作,样式操作
      • 节点属性,节点层级
      • 动态创建元素
      • 事件:注册事件的方式、事件的三个阶段、事件对象

变量

变量是计算机内存中存储数据的标识符,根据变量名称可以获取内存中存储的数据。

驼峰命名法。

数据类型

值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。引用数据类型:对象(Object)、数组(Array)、函数(Function)。

简单类型和复杂类型的区别

基本类型又叫做值类型,复杂类型又叫做引用类型

值类型:简单数据类型,基本数据类型,在存储时,变量中存储的是值本身,因此叫做值类型。

引用类型:复杂数据类型,在存储是,变量中存储的仅仅是地址(引用),因此叫做引用数据类型。

  • 堆和栈

    堆栈空间分配区别:
      1、栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。 
      2、堆(操作系统): 存储复杂类型(对象),一般由程序员分配释放, 若程序员不释放,由垃圾回收机制回收。
    

数据类型转换

转换成字符串类型

  • toString()

    var num = 5;
    console.log(num.toString());
    
  • String()

    String()函数存在的意义:有些值没有toString(),这个时候可以使用String()。比如:undefined和null
    
  • 拼接字符串方式

    num + "",当 + 两边一个操作符是字符串类型,一个操作符是其它类型的时候,会先把其它类型转换成字符串再进行字符串拼接,返回字符串

转换成数值类型

  • Number()

    Number()可以把任意值转换成数值,如果要转换的字符串中有一个不是数值的字符,返回NaN
    
  • parseInt()

    var num1 = parseInt("12.3abc");  // 返回12,如果第一个字符是数字会解析知道遇到非数字结束
    var num2 = parseInt("abc123");   // 返回NaN,如果第一个字符不是数字或者符号就返回NaN
    
  • parseFloat()

    parseFloat()把字符串转换成浮点数
    parseFloat()和parseInt非常相似,不同之处在与
    	parseFloat会解析第一个. 遇到第二个.或者非数字结束
    	如果解析的内容里只有整数,解析成整数
    
  • +,-0等运算

    var str = '500';
    console.log(+str);		// 取正
    console.log(-str);		// 取负
    console.log(str - 0);
    

转换成布尔类型

  • Boolean()

0 ''(空字符串) null undefined NaN 会转换成false 其它都会转换成true

运算符的优先级

优先级从高到底
   1. () 优先级最高
   2. 一元运算符  ++   --   !
   3. 算数运算符  先*  /  %   后 +   -
   4. 关系运算符  >   >=   <   <=
   5. 相等运算符   ==   !=    ===    !==
   6. 逻辑运算符 先&&   后||
   7. 赋值运算符

程序三种结构

  • 顺序结构
  • 分支结构 if else、switch case
  • 循环结构 while、do..while、for循环

调试

  • 过去调试JavaScript的方式
    • alert()
    • console.log()
  • 断点调试
  • 调试中的相关操作
Watch: 监视,通过watch可以监视变量的值的变化,非常的常用。
F10: 程序单步执行,让程序一行一行的执行,这个时候,观察watch中变量的值的变化。
F8:跳到下一个断点处,如果后面没有断点了,则程序执行结束。

函数

把一段相对独立的具有特定功能的代码块封装起来,形成一个独立实体,就是函数 函数的作用就是封装一段代码,可以重复使用

arguments的使用

JavaScript中,arguments对象是比较特别的一个对象,实际上是当前函数的一个内置属性。也就是说所有函数都内置了一个arguments对象,arguments对象中存储了传递的所有的实参。arguments是一个伪数组,因此及可以进行遍历

Image.png

匿名函数

匿名函数:没有名字的函数

匿名函数如何使用:

将匿名函数赋值给一个变量,这样就可以通过变量进行调用
匿名函数自调用

自调用函数

匿名函数不能通过直接调用来执行,因此可以通过匿名函数的自调用的方式来执行

(function () {
  alert(123);
})();

函数是一种数据类型

function fn() {}
console.log(typeof fn);
  • 函数作为参数

因为函数也是一种类型,可以把函数作为两一个函数的参数,在另一个函数中调用

  • 函数做为返回值

因为函数是一种类型,所以可以把函数可以作为返回值从函数内部返回。

块级作用域

任何一对花括号({和})中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。

作用域链

只有函数可以制造作用域结构, 那么只要是代码,就至少有一个作用域, 即全局作用域。凡是代码中有函数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。将这样的所有的作用域列出来,可以有一个结构: 函数内指向函数外的链式结构。就称作作用域链。

预解析

JavaScript代码的执行是由浏览器中的JavaScript解析器来执行的。JavaScript解析器执行JavaScript代码的时候,分为两个过程:预解析过程和代码执行过程

预解析过程:

  1. 把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值。
  2. 把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用。
  3. 先提升var,在提升function。

变量提升

  • 变量提升

    定义变量的时候,变量的声明会被提升到作用域的最上面,变量的赋值不会提升。

  • 函数提升

    JavaScript解析器首先会把当前作用域的函数声明提前到整个作用域的最前面

对象

new关键字

new在执行时会做四件事情

new会在内存中创建一个新的空对象
new会让this指向这个新的对象
执行构造函数  目的:给这个新对象加属性和方法
new会返回这个新对象

this详解

  1. 函数在定义的时候this是不确定的,只有在调用的时候才可以确定
  2. 一般函数直接执行,内部this指向全局window
  3. 函数作为一个对象的方法,被该对象所调用,那么this指向的是该对象
  4. 构造函数中的this其实是一个隐式对象,类似一个初始化的模型,所有方法和属性都挂载到了这个隐式对象身上,后续通过new关键字来调用,从而实现实例化

创建对象的四种方式

  • 对象字面量
  • 通过构造函数Object()来创建
  • 工厂函数
  • 自定义构造函数
// 自定义构造函数
// 帕斯卡命名 第一个单词的第一个字母大写,后续每一个单词的第一个字母都大写

Image.png

Image [2].png

// for in 可以遍历对象的成员
let obj = { 
    name: 'zs',
    age: 18,
    sex: '男',
    sayHi: function() {
    console.log(this.name + ':hello');
    }
}
// 输出对象属性和值
for(let key in obj) {
    console.log(key + '---' + obj[key]);
}

// 删除对象

delete obj.name;

内置对象

JavaScript中的对象分为3种:内置对象、自定义对象、浏览器对象

JavaScript 提供多个内置对象:Math/Array/Date....

MDN

数组

  • 创建数组对象的两种方式

    • 字面量方式
    • new Array()
  • 检测一个对象是否是数组

    • instanceof
    • Array.isArray() HTML5中提供的方法,有兼容性问题

    函数的参数,如果要求是一个数组的话,可以用这种方式来进行判断

  • toString()/valueOf()

    • toString() 把数组转换成字符串,逗号分隔每一项
    • valueOf() 返回数组对象本身
  • 数组常用方法

    push()、shift()、unshift()、reverse()、sort()、splice()、indexOf()

数组常用方法

栈操作:先进后出

  • push() 向数组插入元素
  • pop() 取出数组最后一个元素

队列操作:先进先出

  • push() 向数组插入元素
  • shift() 取出数组第一个元素
  • unshift() 插入一个元素到数组首位

排序方法:

  • reverse() 翻转数组,(第 i 个和第 length - i - 1个进行交换)
  • sort() 对数组排序,默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的

操作方法:

  • concat() 把参数拼接到当前数组
  • slice(start,end) 截取新数组
  • splice() 插入/删除/替换当前数组某些项目
  • join('-') 用参数拼接字符串
// 清空数组:
let arr = [1,2,3];
arr = [];   // 第一种(推荐)
arr.length() = 0; //第二种

// 第一个参数,从什么位置开始删除 索引
//第二个参数 ,删除多少个元素
arr.splice(0,arr.length);

// 1-2-3
arr.join("-");

位置方法:

  • indexOf() //如果没找到返回-1
  • lastIndexOf() //如果没找到返回-1

迭代方法:自带循环;html5中提供

  • every()
  • filter()
  • forEach()
  • map()
  • some()

字符串常用方法

基本包装类型

基本类型包装成复杂类型 String Number Boolean (一般情况下不会使用Number Boolean 基本包装类型)

PrimitiveValue 原始值

字符串不可变

给字符串重新赋值,内存要重新分配空间

字符串的所有方法都不会修改原始字符串本质(字符串不可变性)

字符串的方法

// 1 字符方法
charAt()    	//获取指定位置处字符
charCodeAt()  	//获取指定位置处字符的ASCII码
str[0]   		//HTML5,IE8+支持 和charAt()等效
// 2 字符串操作方法
concat()   		//拼接字符串,等效于+,+更常用
slice()    		//从start位置开始,截取到end位置,end取不到
substring() 	//从start位置开始,截取到end位置,  end取不到
substr()   		//从start位置开始,截取length个字符
// 3 位置方法
indexOf()   	//返回指定内容在元字符串中的位置
lastIndexOf() 	//从后往前找,只找第一个匹配的
// 4 去除空白   
trim()  		//只能去除字符串前后的空白
// 5 大小写转换方法
to(Locale)UpperCase() 	//转换大写
to(Locale)LowerCase() 	//转换小写
// 6 其它
search() 
replace()
split()
去除字符串中的空白
let s = '  sa afa   ag  1  3';
let arr = s.split(" ");
console.log(arr);
console.log(s.trim());
//split() 配合 join() 取出空白 ;trim()只能去除数组前后空白
console.log(arr.join(""));
求字符串中次数出现最多的字符和次数
// 求字符串中次数出现最多的字符和次数
let s = "afavaooaogjvoagaj";
// 最大值的字符
let ch;
// 统计最大值
let num;
// 记录字符串中每一个字符出现的次数
let o = {};
for (let i = 0; i < s.length; i++) {
  let item = s.charAt(i);
  if (o[item]) {
    //   对象中有该属性
    o[item]++;
  } else {
    //   对象中没有该属性
    o[item] = 1;
  }
}
// 假设最大值是1
num = 1;
for (let key in o) {
  if (num < o[key]) {
    num = o[key];
    ch = key;
  }
}
console.log(num);
console.log(ch);
获取url中的参数
let url = 'https://www.bilibili.com/login?name=zs&age=18&a=1&b=2';
let o = {};

function getParams(url) {
    let index = url.indexOf('?') + 1;
    let params = url.substr(index);
    let arr = params.split('&');
    for (let i = 0; i < arr.length; i++) {
        let tmpArr = arr[i].split('=');
        let key = tmpArr[0];
        let value = tmpArr[1];
        o[key] = value;
    }
    return o;
}

let obj = getParams(url);
console.log(obj);
console.log(obj.name);

DOM

DOM是文档对象模型:当网页被加载时,浏览器会创建页面的文档对象模型。

  • 文档:一个网页可以称为文档
  • 节点:网页中的所有内容都是节点(标签、属性、文本、注释等)
  • 元素:网页中的标签
  • 属性:标签的属性

DOM经常进行的操作

  • 获取元素
  • 对元素进行操作(设置其属性或调用其方法)
  • 动态创建元素
  • 事件(什么时机做相应的操作)

根据id获取元素

var div = document.getElementById('main');
console.log(div);

// 获取到的数据类型 HTMLDivElement,对象都是有类型的

根据标签名获取元素

var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
  var div = divs[i];
  console.log(div);
} 

根据name获取元素*

var inputs = document.getElementsByName('hobby');
for (var i = 0; i < inputs.length; i++) {
  var input = inputs[i];
  console.log(input);
}

根据类名获取元素*

var mains = document.getElementsByClassName('main');
for (var i = 0; i < mains.length; i++) {
  var main = mains[i];
  console.log(main);
}

根据选择器获取元素*

var text = document.querySelector('#text');
console.log(text);

var boxes = document.querySelectorAll('.box');
for (var i = 0; i < boxes.length; i++) {
  var box = boxes[i];
  console.log(box);
}

自定义属性操作

  • getAttribute() 获取标签行内属性
  • setAttribute() 设置标签行内属性
  • removeAttribute() 移除标签行内属性
  • 与element.属性的区别: 上述三个方法用于获取任意的行内属性。

样式操作

  • 使用style方式设置的样式显示在标签行内
var box = document.getElementById('box');
box.style.width = '100px';
box.style.height = '100px';
box.style.backgroundColor = 'red';
  • 注意

    通过样式属性设置宽高、位置的属性类型是字符串,需要加上px

类名操作

  • 修改标签的className属性相当于直接修改标签的类名
var box = document.getElementById('box');
box.className = 'show';

创建元素的三种方式

document.write()
document.write('新设置的内容<p>标签也可以生成</p>'); //当点击单击按钮事件时,会把之前整个页面覆盖掉。
innerHTML
var box = document.getElementById('box');
box.innerHTML = '新内容<p>新标签</p>';

// 优化1
        let datas = ['西施', '貂蝉', '陈雨', '落雁'];
        let box = document.getElementById('box');
        let btn = document.getElementById('btn');
        btn.onclick = function () {
            let html = '<ul>';
            for (let i = 0; i < datas.length; i++) {
                let data = datas[i];
                html += '<li>' + data + '</li>';
            }
            html += '</ul>';

            box.innerHTML = html;
        }

// 优化2 性能最高
        let datas = ['西施', '貂蝉', '陈雨', '落雁'];
        let box = document.getElementById('box');
        let btn = document.getElementById('btn');
        btn.onclick = function () {
            let array = [];
            array.push('<ul>')
            for (let i = 0; i < datas.length; i++) {
                let data = datas[i];
                array.push('<li>' + data + '</li>');
            }
            array.push('</ul>');

            box.innerHTML = array.join(''); //当li生成页面后,再从页面上查找li注册事件
        }
document.createElement()
var div = document.createElement('div'); //内存中动态创建一个元素,页面上没有
document.body.(div); //放在页面上

节点属性

  • nodeType 节点的类型
    • 1 元素节点
    • 2 属性节点
    • 3 文本节点
  • nodeName 节点的名称(标签名称)
  • nodeValue 节点值
    • 元素节点的nodeValue始终是null
节点层级
var box = document.getElementById('box');
console.log(box.parentNode); //父节点
console.log(box.childNodes); //子节点集合
console.log(box.children); //获取子元素节点集合
console.log(box.nextSibling); //下一个兄弟节点
console.log(box.previousSibling); //上一个兄弟节点
console.log(box.nextElementSibling); //下一个兄弟元素
console.log(box.previousElementSibling); //上一个兄弟元素
console.log(box.firstChild); //获取第一个节点
console.log(box.lastChild); //获取最后一个节点
console.log(box.firstElementChild); //返回第一个子元素
console.log(box.lastElementChild) //返回最后一个子元素
  • 注意

    childNodes和children的区别,childNodes获取的是子节点,children获取的是子元素

    nextSibling和previousSibling获取的是节点,获取元素对应的属性是nextElementSibling和previousElementSibling获取的是元素

    ​ nextElementSibling和previousElementSibling有兼容性问题,IE9以后才支持

  • 总结

节点操作,方法
	appendChild()
	insertBefore()
	removeChild()
	replaceChild()
节点层次,属性
	parentNode
	childNodes
	children
	nextSibling/previousSibling
	firstChild/lastChild

事件

事件:触发-响应机制

事件三要素

  • 事件源:触发(被)事件的元素
  • 事件名称: click 点击事件
  • 事件处理程序:事件触发后要执行的代码(函数形式)
事件的基本使用
var box = document.getElementById('box');
box.onclick = function() {
  console.log('代码会在box被点击后执行');  
};
注册/移除事件的三种方式
var box = document.getElementById('box');

// 无法给同一个对象的同一个事件注册多个事件处理函数
box.onclick = function () {
  console.log('点击后执行');
};
box.onclick = null;

// 可以给同一个对象的同一个事件注册多个事件处理函数,有浏览器兼容性问题,IE9以后才支持
box.addEventListener('click', eventCode, false);

// 想要移除事件,注册事件的时候不能使用匿名函数(要给函数命名)
box.removeEventListener('click', eventCode, false);

// IE9以前特有方法
box.attachEvent('onclick', eventCode);
box.detachEvent('onclick', eventCode);

function eventCode() {
  console.log('点击后执行');
}

兼容代码

function addEventListener(element, type, fn) {
  if (element.addEventListener) {
    element.addEventListener(type, fn, false);
  } else if (element.attachEvent){
    element.attachEvent('on' + type,fn);
  } else {
    element['on' + type] = fn;
  }
}

function removeEventListener(element, type, fn) {
  if (element.removeEventListener) {
    element.removeEventListener(type, fn, false);
  } else if (element.detachEvent) {
    element.detachEvent('on' + type, fn);
  } else {
    element['on'+type] = null;
  }
}
事件的三个阶段
  1. 捕获阶段

  2. 当前目标阶段 (执行当前点击的事件,attachEvent()只有这个阶段)

  3. 冒泡阶段(子元素和父元素都定义了事件,当触发子元素的事件时,父元素的事件也会触发)

阻止事件传播的方式
  • 标准方式 event.stopPropagation(); 取消冒泡
  • IE低版本 event.cancelBubble = true; 标准中已废弃
事件对象的属性和方法
  • event.type 获取事件类型

  • 事件对象.eventPhase属性可以查看事件触发时所处的阶段

  • clientX/clientY 所有浏览器都支持,窗口位置

  • pageX/pageY IE8以前不支持,页面位置,滚动条

  • event.target || event.srcElement 用于获取触发事件的元素

  • event.currtentTarget 真正点击的元素

  • event.preventDefault() 取消默认行为 (相当 return false;)

  • 事件对象.offsetLeft , 事件对象.offsetTop 获取盒子在页面上的位置

  • event.keycode 键盘码 0 - 18,9 - 57 后退键 - 8 可用于处理文本输入只能是数字

常用的鼠标和键盘事件
  • onmouseup 鼠标按键放开时触发
  • onmousedown 鼠标按键按下触发
  • onmousemove 鼠标移动触发
  • onkeyup 键盘按键按下触发
  • onkeydown 键盘按键抬起触发

BOM

BOM(Browser Object Model) 是指浏览器对象模型,浏览器对象模型提供了独立于内容的、可以与浏览器窗口进行互动的对象结构。BOM由多个对象组成,其中代表浏览器窗口的Window对象是BOM的顶层对象,其他对象都是该对象的子对象。

对话框

  • alert(message)
  • prompt(text,defaultText)
  • confirm(message)

页面加载事件

  • onload
window.onload = function () {
  // 当页面加载完成执行
  // 当页面完全加载所有内容(包括图像、脚本文件、CSS 文件等)执行
  // 不仅是window有,元素也有onload属性
  // f5 先卸载页面 在 重新加载页面
}
  • onunload
window.onunload = function () {
  // 在onunload中所有对话框都无法使用 window对象被冻结
  // 当用户退出页面时执行
}

定时器

setTimeout()和clearTimeout()

在指定的毫秒数到达之后执行指定的函数,只执行一次

// 创建一个定时器,1000毫秒后执行,返回定时器的标示
var timerId = setTimeout(function () {
  console.log('Hello World');
}, 1000);

// 取消定时器的执行
clearTimeout(timerId);

setInterval()和clearInterval()

定时调用的函数,可以按照给定的时间(单位毫秒)周期调用函数

// 创建一个定时器,每隔1秒调用一次
var timerId = setInterval(function () {
  var date = new Date();
  console.log(date.toLocaleTimeString());
}, 1000);

// 取消定时器的执行
clearInterval(timerId);

URL统一资源定位符 (Uniform Resource Locator)

  • URL的组成
scheme://host:port/path?query#fragment
http://www.itheima.com:80/a/b/index.html?name=zs&age=18#bottom
scheme:通信协议
	常用的http,ftp,maito等
host:主机
	服务器(计算机)域名系统 (DNS) 主机名或 IP 地址。
port:端口号
	整数,可选,省略时使用方案的默认端口,如http的默认端口为80path:路径
	由零或多个'/'符号隔开的字符串,一般用来表示主机上的一个目录或文件地址。
query:查询
	可选,用于给动态网页传递参数,可有多个参数,用'&'符号隔开,每个参数的名和值用'='符号隔开。例如:name=zs
fragment:信息片断
	字符串,锚点.

history对象

  • back()
  • forward()
  • go() go(-1) 后退 go(1) 前进

navigator对象

  • userAgent userAgent 属性是一个只读的字符串,声明了浏览器用于 HTTP 请求的用户代理头的值。

偏移量

  • offsetParent用于获取定位的父级元素
  • offsetParent和parentNode的区别