JavaScript笔记
变量作用域:
1、全局变量:在全局作用域下声明的变量
在函数内部没有声明直接赋值的变量也是属于全局变量
全局变量:只有浏览器关闭的时候才会销毁,比较占内存资源
局部变量 :当我们程序执行完毕就会销毁,比较节约内存资源
作用域链:内部函数访问外部函数的变量,采取的是链式查找的方式来决定取哪个值 这种结构我们称之为作用域链 就近原则
var申明变量会存在变量提升
js引擎运行js分为两步:
1、解析 2、代码执行
(1)预解析:js 引擎会把 js 里面所有的 var 和 function 提升到当前作用域的最前面。
(2)代码执行:按照代码速写的顺序从上往下执行
2、预解析分为变量预解析(变量提升)和函数预解析(函数提升)
(1)变量提升:就是把所有的变量声明提升到当前的作用域最前面 不提升赋值操作
(2)函数提升:就是把所有的函数声明提升到当前作用域的最前面 不调用函数
构造函数:
1.构造函数名字首字母要大写
2.构造函数不需要return 就可以返回结果
3.调用构造函数必须使用new
4.只要new Star() 调用函数就创建了一个对象
object.setAttribute(sName, vValue [, iFlags]):给元素设置自定义属性
参数:
sName
必填项. String类型,属性名
vValue
必填项. 为属性指定的变量,可以为string, number, 或者 Boolean类型
iFlags
选填. 下面指定的两种 Integer 类型的标志
0
覆盖同名属性.
1
默认值. 为属性添加指定的值.
let attribute = element.getAttribute(attributeName);查看元素的自定义属性
attribute
是一个包含attributeName
属性值的字符串。attributeName
是你想要获取的属性值的属性名称。- 返回属性的值
添加事件监听:addEventlistener(事件,函数)
事件捕获和事件冒泡
事件捕获:发生事件时从document
一级一级往下寻找目标
事件冒泡:当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发,这一过程被称之为事件冒泡(简单理解:当一个元素触发事件后,会依次向上调用所有父级元素的同名事件)
阻止事件冒泡:
- 因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素
- 若想把事件就限制在当前元素内,就需要阻止事件流动
- 阻止事件流动需要拿到事件对象
- 语法:
事件对象.stopPropagation()
- 此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效
事件委托
-
事件委托是利用事件流的特征解决一些开发需求的知识技巧
-
优点:给父级元素添加事件(可以提高性能)
-
原理:事件委托其实是利用事件冒泡的特点,给父元素添加事件,子元素可以触发
-
实现:
事件对象.target
可以获得真正触发事件的元素// 具体案例代码见:/Users/shenguangmin/资料/代码文件/javascript/28.事件委托.html // 不要给每个小li注册事件了, 而是把事件委托给他的爸爸 // 事件委托是给父级添加事件, 而不是给儿子添加事件 let ul = document.querySelector('ul') ul.addEventListener('click',function(e){ // console.log(e); // // 得到当前点击的具体元素 // console.log(e.target); //对应的li标签变成红色 e.target.style.color = 'red'; })
事件对象的常用对象方法:
阻止冒泡传播:stopPropagation()
获取当前的被操作的对象:target()
阻止默认事件的发生:preventDefult()
等待页面内容全部记载完毕:load()
等待DOM加载完毕,不包含图、css等等:DOMContentLoaded()
定时器(只调用一次):setTimeout(函数,延时时间)
var t1 = setTimeout(function () {
console.log('时间到')
}, 3000)
清除定时器:clearTimeout(定时器名称)
/clearInterval(定时器名称)
var t1 = setTimeout(function () {
console.log('时间到')
}, 3000)
var btn = document.querySelector('button')
btn.addEventListener('click', function () {
clearTimeout(t1)
console.log('已阻止');
})
间歇函数 setInterval(函数,间隔时间)
每隔一段时间执行setInterval中的函数
let s1 = setInterval(function () {
console.log('持续输出')
}, 1000)
关闭定时器 clearInterval(变量名)
let 变量名 = setInterval(函数,间隔时间)
clearInterval(变量名)
offset和style的区别
offset可以得到任意样式表中的样式值
所以我们想要获取元素大小位置,用offset更合适
style只能得到行内样式表中的样式值
所以我们想要给元素更改值,则需要用style改变
立即执行函数
作用:创建一个独立的作用域,避免了命名冲突问题
两种写法:(function(){})() 或者 (function(){}())
获取dom元素
let box = document.querySelector('div') //返回一个dom对象
修改dom的样式 (className属性)
dom.className = 'newClassName'
格式化输出
let i = 'tuoni'
console.log(`名字叫${i}`) //用反引号包裹
事件监听 addEventListener('事件',触发事件后执行的函数)
let btn = document.querySelector('button')
btn.addEventListener('click',function () {
console.log('被点击了!')
)
回调函数
如果将函数A作为参数传递给函数B时,我们称函数A为回调函数
简单理解:当一个函数被当做参数来传递给另外一个函数的时候,这个函数就是回调函数
常见的使用场景:
function fn(){
console.log('我是回调函数...')
}
// fn传递给了setInterval,fn就是回调函数
setInterval(fn, 1000)
事件对象
1.什么是事件对象?
也是个对象,这个对象是有事件触发时的相关信息
2.事件对象在哪里?
在事件绑定的回调函数的第一个参数就是事件对象
元素.addEventListener('click', function(e){}) // 这里的e就是鼠标的事件对象
常用的事件对象中的属性:
type
:获取当前的事件类型
clientX
/clientY
:获取光标相当于浏览器可见窗口左上角的位置
offsetX
/offsetY
:获取光标相当于当前DOM元素左上角的位置
key
:用户按下的键盘键的值,现在不提倡使用keyCode
srcroll家族
scrollWidth
和scrollHeight
:获取元素的内容总宽高(不含滚动条)返回值不带单位
scrollLeft
和 scrollTop
:获取元素内容往左、往上滚出去看不到的距离
:属性可以修改
offset家族
offsetWidth
和offsetHeight
:获取元素的自身宽高、包含元素自身设置的宽高、padding、border
offsetLeft
和offsetTop
:获取元素距离自己定位父级元素的左、上距离
注意:offsetLeft
和offsetTop
是只读属性不能修改
client家族
clientWidth
和clientHeight
:获取元素的可见部分宽高(不含滚动条和边框)
clientLeft
和clientTop
:获取元素的位置
js中的同步异步执行顺序(即事件循环)
1.先执行执行栈
中的同步任务
2.异步任务放入任务队
中
3.一旦执行栈
中的所有同步任务执行完毕,系统就会按次序读取任务队列
中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈
,开始执行
注意:如果存在多个异步任务,会根据异步任务的实际情况来决定他们的优先级
location对象
location的数据类型是对象,它拆分并保存了URL地址的各个组成部分
常用属性和方法:
href
属性获取完整的URL地址,对其赋值时用于地址跳转
search
属性获取地址中携带的参数,符号?后面部分
hash
属性获取地址中的哈希值,符号#后面部分
reload
方法用来刷新当前页面,传入参数true时表示强制刷新
本地存储 localStorage
- 存储简单数据类型
存储数据:localStorage.setItem('key', value)
读取数据:localStorage.getItem('key')
删除数据:localStorage.removeItem('key')
-
存储复杂数据类型
本地只能存储字符串,无法存储复杂数据类型,需要将复杂数据类型转换成JSON字符串,再存储到本地
JSON.stringify(复杂数据类型)
JSON.stringify(复杂数据类型)
将复杂数据类型转换成JSON字符串 存储
到本地存储中
JSON.parse(JSON字符串)
将JSON字符串转换成对象 取出
时候使用
正则
定义:用let re = /正则表达式/
的方式定义正则表达式,得到一个正则对象
test()
: 用于判断是否有符合规则的字符串,返回的是布尔值
let re = /.*/
console.log(re.test('slasjdf')); // true
exec()
: 用于查找符合规则的字符串,返回一个数组
let re = /.*/
console.log(re.exec('slasjdf')); // ['slasjdf']
面向对象(类,es6新增)
创建类的关键字:class
类中的构造方法:constructor()
实例化对象的关键字:new
class Student{
constructor(name, age){
this.name = name
this.age = age
}
}
let user = new Student('tuoni', 18)
类的继承
继承的关键字:extends
class Father{
constructor(x, y){
this.x = x;
this.y = y;
}
sum(){
console.log(this.x + this.y);
}
say(){
console.log('我是爸爸')
}
}
class Son extends Father{
constructor(x, y){
super(x, y); //调用了父类中的构造函数,必须放在子类中this之前
// super.say() // 调用了父类中的其他方法
}
}
var son = new Son(1, 2);
son.sum();
插入新的标签
element.insertAdjacentHTML(position, text)
-
position
一个
DOMString
,表示插入内容相对于元素的位置,并且必须是以下字符串之一:'beforebegin'
:元素自身的前面。'afterbegin'
:插入元素内部的第一个子节点之前。'beforeend'
:插入元素内部的最后一个子节点之后。'afterend'
:元素自身的后面。 -
text
是要被解析为 HTML 或 XML 元素,并插入到 DOM 树中的
DOMString
。
构造函数创建对象(es6之前)
- 构造函数
构造函数
是一种特殊的函数,主要用来初始化对象即为对象成员变量赋初始值,它总与new一起使用,我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
-
new在执行时会做四件事情
-
在内存中创建一个新的空对象
-
让this指向这个新的对象
-
执行构造函数里面的代码,给这个新对象添加属性和方法
-
放回这个新对象(所以构造函数里面不需要 return)
-
function Student(name, age) {
this.name = name;
this.age = age;
this.run = function(){
console.log('跑');
}
}
var student1 = new Student('tuoni', 19);
var student2 = new Student('张学友', 29);
console.log(student1.age);
console.log(student2.age);
console.log(student1.run());
console.log(student2.run());
-
实例成员与静态成员
- 实例成员就是构造函数内部通过this添加的成员 name age run就是实例成员,实例成员只能通过实例化的对象来访问
- 静态成员 在构造函数本身上添加的成员 sex 就是静态成员,静态成员只能通过构造函数来访问
function Student(name, age) { this.name = name; this.age = age; this.run = function(){ console.log('跑'); } } var student1 = new Student('tuoni', 19); Student.sex = '男'; // 静态成员 console.log(Student.sex); // 只能通过构造函数访问
缺点:使用构造函数创建对象会浪费很多的内存空间,使用
new
实例化对象时会开辟一个内存空间将基本数据类型存储在这个内存空间中,然后会开辟另一个新的内存空间用来保存复杂数据类型(如:方法),那么多个对象访问的同名方法并不是在同一内存空间中,而是各自的内存空间。解决:使用原型对象保存公共方法
构造函数原型 prototype
构造函数通过原型分配的函数是所有对象所共享的
JavaScript规定,每一个构造函数都有一个prototype
属性,指向的是另一个对象,注意这个prototype
就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有
我们可以把那些不变的方法,直接定义在prototype
对象上,这样所有对象的实例就可以共享这些方法。
function Student(name, age) {
this.name = name;
this.age = age;
Student.prototype.run = function () {
console.log('跑');
}
}
var student1 = new Student('tuoni', 19);
var student2 = new Student('孙悟空', 29);
student1.run(); // 跑
student2.run(); // 跑
console.log(student1.run === student2.run); // true 说明内存地址相同
对象原型
为什么studnet1对象可以直接调用prototype对象中的run方法?
因为每个对象身上会有一个__proto__
属性,这个属性等价于构造函数中prototype
属性
方法的查找规则:首先看student1对象身上是否有run
方法,如果有就执行这个对象上的run
方法,如果没有,因为有__proto__
的存在,就去构造函数原型对象prototype
身上去查找
-
constructor
是原型对象prototype
中的一个属性,记录着对象的构造函数(由哪个构造函数所创建)如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用
constructor
指回原来的构造函数如下:function Student(name, age) { this.name = name; this.age = age; Student.prototype = { constructor: Student, run: function () { console.log('跑'); }, sing: function () { console.log('唱歌'); } } } var student1 = new Student('tuoni', 19); console.log(student1.__proto__.constructor); // 构造函数Student
原型链
每个实例对象都会自带一个__proto__
属性(原型),在上图中Star原型对象中也出现了__proto__
属性(原型),说明Star的原型对象也是一个对象,那么Star原型对象__proto__
属性(原型)来自于Object原型对象中,最终Object原型对象中的__proto__
指向null
JavaScript的成员查找机制(规则)
- 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
- 如果没有就查找它的原型(也就是
__proto__
指向的prototype原型对象) - 如果还没有就查找原型对象的原型(Object的原型对象)
- 以此类推一直找到Object为止(null)
__proto__
对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条线路
call方法改变this指向
fn.call(obj,形参1,形参2)
fn
: 需要执行的函数
obj
: 需要指向的对象
形参 :被执行函数所需要的参数
类的本质
class
本质还是function
- 类的所有方法都定义在类的
prototype
属性上 - 类创建的实例,里面也有
__proto__
指向类的prototype
原型对象 - 所以ES6的类他的绝大部分功能,ES5都可以做到,新的
class
写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已 - 所以ES6的类其实就是语法糖
ES5中的新增方法
数组方法
forEach
迭代(遍历)数组
array.forrEach(function(currentValue, index, arr))
currentValue
: 数组当前项的值index
:数组当前项的索引arr
:数组对象本身
var list = [1, 2, 3, 34, 5]
list.forEach(function (value, index, arr) {
console.log(value);
console.log(index);
console.log(arr);
})
filter()
filter()
方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组
array.filter(function(currentValue, index, arr))
- 注意它直接返回一个新的数组
currentValue
: 数组当前项的值index
: 数组当前项的索引arr
: 数组对象本身
var li = [1, 2, 3, 4, 5]
var ar = li.filter(function (value, index, arr) {
return value > 3;
})
console.log(ar); // [4, 5]
some()
some()
方法用于检测数组中的元素时候满足指定条件,通俗点说查找数组中是否有满足条件的元素- 注意它返回值是布尔值,如果查找到这个元素,就返回true,如果查找不到就返回false
- 如果找到第一个满足条件的元素,则终止循环,不在继续查找
currentValue
: 数组当前项的值index
: 数组当前项的索引arr
: 数组对象本身
var li = [1, 2, 3, 4, 5]
var ar = li.some(function (value, index, arr) {
return value > 3;
})
console.log(ar); // true
对象方法
Object.defineProperty()
定义新属性或修改原有的属性
Object.defineProperty(obj, prop, descriptor)
obj
: 被操作的对象
prop
: 属性名
descriptor
: 说明,以对象形式{ }书写
- value : 设置属性的值 默认为undefined
- writable :值是否可以重写。 true|false 默认为false
- enumerable :目标属性是否可以被枚举。true|false 默认为false
- configurable:目标属性是否可以被删除或是否可以再次修改特性true|false默认为false
var obj = {
name: '1231',
age: '232222'
}
Object.defineProperty(obj, 'sex', {
value: '男',
// 如果为false 不允许修改这个属性值
writable:false,
// 如果为false 不允许遍历
enumerable:false,
// 如果为false 不允许删除这个属性 不允许在修改第三个参数里的特性
configurable:false
})
字符串方法
trim()
方法会从一个字符串的两端删除空白字符,并返回一个新的字符串
let st = ' andy '
console.log(st.trim()) // 'andy'
Object.keys(obj)
获取对象中所有的键,以列表的形式返回
var obj = {
name: '1231',
age: '232222'
}
console.log(Object.keys(obj)); //['name', 'age']
bind方法
bind()方法不会调用函数,但是能改变函数内部this指向
fun.bind(thisArg, arg1, arg2, ...)
thisArg
: 在fun函数运行时指定的this值arg1
,arg2
: 传递的其他参数- 返回由指定的this值和初始化参数改造的原函数拷贝
var obj = {
name: 'tuoni',
age: '19'
}
function fn(){
console.log(this);
}
var f = fn.bind(obj)
f() // 指向obj
ES6语法
let 关键字
- 不能重复声明:
let a = 1;
let a = 2; // 报错
-
与var不同,没有变量提升
-
拥有块级作用域(在大括号中的才会存在)
if (true){
let a = 1
}
console.log(a) //报错
-----------------------------
{
let name = 'tuoni'
}
console.log(name) // 报错
const 关键字
-
不能重复声明:
const a = 1; const a = 2; // 报错
-
用来定义常量,定义后不能修改,但是可以修改复杂数据类型
const a = 1; a = 2 ; console.log(a); // 报错 const list = [1,2,3]; list.push(4); console.log(list); // [1,2,3,4]
箭头函数
- 无法改变箭头函数中的this指向,this始终指向函数声明时所在作用域下的this的值
// 普通函数
function a() {
console.log(this);
}
// 箭头函数
let b = () => {
console.log(this)
}
a() // window
b() // window
let obj = {
name: 'tuoni'
}
a.call(obj) // tuoni
b.call(obj) // window
-
不能作为构造函数实例化对象
let Person = (name, age) => { this.name = name; this.age = age; } let me = new Person('tuoni', 19) console.log(me); // 报错
对象定义简写
let obj = {
name,
say(){
console.log('hhhhhh')
}
}
等同于>>>>>>
let obj = {
name: ''
say: function(){
console.log('hhhhhh')
}
}
迭代器(需要自定义遍历数据的时候)
迭代器(iterator
)是一种接口,为各种不同的数据结构提供统一的访问机制,任何数据结构只要部署 iterator
接口(对象中的一个属性),就可以完成遍历操作。
- ES6创造了一种新的遍历命令for...of循环,
iterator
接口主要提供for...of 消费 - 原生具备
iterator
接口的数据(可用for of遍历)- Array
- Arguments
- Set
- Map
- String
- TypeArray
- NodeList
- 工作原理
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的next方法,指针自动指向数据结构的第一个成员
- 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
- 每调用next方法返回一个包含value和done属性的对象
给自定对象添加iterator接口实现for...of遍历:
let obj = {
name: '终极一班',
stus: [
'xiaomin',
'xiaohuam',
'xiaogao',
'tuoni'
],
[Symbol.iterator]() {
// 索引变量
let index = 0;
let _this = this
return {
next: function () {
if (index < _this.stus.length) {
const result = { value: _this.stus[index], done: false }
index++;
return result
} else {
return { value: undefined, done: true }
}
}
}
}
}
// 使用for of 对对象进行自定义遍历
for (const i of obj) {
console.log(i);
}