学会查文档ReferenceAPI函数
进阶版笔记有点乱,可以看百度网盘的JS高级教程md笔记
BOM:浏览器对象模型
window:navigator、location
、history、screen
、document(window下面的对象)
window:load、DOMContentLoaded、beforeunload(离开页面触发)、
dragover、drop
classList:add、remove、toggle、contains
自定义属性:data-xxx、dataset
ES6
中{}大括号包裹的代码块等价于局部作用域局部变量,括号外不能使用,let
声明的变量产生块作用域(window对象下没有),var
不会产生块作用域事全局变量(window对象里面有)const
声明的常量也会产生块作用域,推荐使用 let
或 const
(const声明的不能改变,其余和let没什么区别)arguments
是函数内部内置的伪数组变量,它包含了调用函数时传入的 所有
实参,作用是动态获取函数的实参。<script>
// 求生函数,计算所有参数的和
function sum() {
// console.log(arguments);
let s = 0;
for(let i = 0; i < arguments.length; i++) {
s += arguments[i];
}
console.log(s);
}
// 调用求和函数
sum(5, 10); // 两个参数
sum(1, 2, 4); // 两个参数
script>
代码在执行之前,任何一个作用域都要预解析,解析变量和函数(也就是只解析定义的函数,不解析调用的和回调函数,其实原理和BOM里面的队列栈线程的同步异步类似)。这样,预解析解析完了再执行
,即便先调用,再声明,不会报错,是undefined,函数内部还有就继续被解析,但是let的声明不会被解析,就会报错
变量:带有申明的变量才会进行变量解析,变量提升,JS会把申明的变量,提升到当前作用域的最前面,只申明不赋值
函数:代有函数名的变量才会进行函数解析(回调函数和函数表达式不会被解析),函数提升,JS会把带有名字的函数,提升到当前作用域最前面,只定义不调用
<script>
// 调用函数
foo();
// 声明函数(会解析,被提前)
function foo() {
console.log('声明之前即被调用...');
}
// 不存在提升现象
bar();
var bar = function () {
console.log('函数表达式不存在提升现象...');
}
script>
死区:let声明之前的,不会被解析,报错!!
但是下面代码如果var则可以,输出undefined
下面练习,
闭包是一种比较特殊和函数,使用闭包能够访问函数作用域中的变量。从代码形式上看闭包是一个做为返回值的函数,
闭包:一个作用域有权访问另外一个作用域的局部变量,
好处:可以把一个变量使用范围延伸
<script>
function foo() {
let i = 0;
// 函数内部分函数
function bar() {
console.log(++i);
}
// 将函数做为返回值
return bar;
}
// fn 即为闭包函数
let fn = foo();
fn(); // 1
script>
箭头函数是一种声明函数的简洁语法,它与普通函数并无本质的区别,差异性更多体现在语法格式上。
()
{}
,并自动做为返回值被返回arguments
,只能使用 ...
动态获取实参<script>
// 箭头函数
let foo = () => {
console.log('^_^ 长相奇怪的函数...');
}
// 调用函数
foo();
// 更简洁的语法
let form = document.querySelector('form');
form.addEventListener('click', ev => ev.preventDefault());
script>
2.函数体只返回一行时,可以省略大括号和return
箭头函数的使用场景如下(定时器的this也是指向window或者事件源的)
3.箭头函数没有预解析,所以必须先定义再调用
5.谁调用箭头函数,箭头函数里面的this就指向谁(箭头函数认为没有this,所以里面不要使用this而使用元素.什么)
匿名函数,事件源里面的这个this指向的事件源(事件调用者),也就是btn
对象里面含有属性,也有方法,调用的时候是对象调用,所以this指向对象
箭头函数没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象),此处指父级作用域,而不是执行时的对象
, 定义它的时候,可能环境是window; 箭头函数可以方便地让我们在 setTimeout ,setInterval中方便的使用this
下面是箭头函数和匿名函数的比较!!!(匿名会指向当前调用的对象,但是箭头函数多数为window)
var s = 21;
const obj = {
s: 42,
m: () => console.log(this.s),
k: function(){console.log(this.s)}
};
obj.m(); // 21
obj.k(); // 42
<button>789798button>
<script>
let btn = document.querySelector('button')
// 匿名函数的调用
// btn.addEventListener('click',function(){
// //console.log(this); //
// this.style.backgroundColor = 'red' //按钮变红
// },false);
//箭头函数
btn.addEventListener('click',() => {
console.log(this); //window{window:.....}
this.style.backgroundColor = 'red' //报错
},false);
script>
如果上图是匿名函数,两个this分别指向调用者,也就是window和document
call
方法能够在调用函数的同时指定 this
的值call
方法调用函数时,第1个参数为 this
指定的值call
方法的其余参数会依次自动传入函数做为函数的参数<script>
// 普通函数
function sayHi() {
console.log(this);
}
let user = {
name: '小明',
age: 18
}
let student = {
name: '小红',
age: 16,
}
// 调用函数并指定 this 的值
sayHi.call(user); // this 值为 user
sayHi.call(student); // this 值为 student
// 求和函数
function counter(x, y) {
return x + y;
}
// 调用 counter 函数,并传入参数
let result = counter.call(null, 5, 10);
console.log(result);
script>
使用 call
方法调用函数,同时指定函数中 this
的值,改变原有的this指向
这样obj就是o对象了
<script>
// 普通函数
function sayHi() {
console.log(this);
}
let user = {
name: '小明',
age: 18
}
let student = {
name: '小红',
age: 16
}
// 调用函数并指定 this 的值
sayHi.apply(user); // this 值为 user
sayHi.apply(student); // this 值为 student
// 求和函数
function counter(x, y) {
return x + y;
}
// 调用 counter 函数,并传入参数
let result = counter.apply(null, [5, 10]);
console.log(result);
script>
apply
方法能够在调用函数的同时指定 this
的值apply
方法调用函数时,第1个参数为 this
指定的值apply
方法第2个参数为数组,数组的单元值依次自动传入函数做为函数的参数bind
方法并不会调用函数,而是创建一个指定了 this
值的新函数,要执行就使用()进行调用,此时对象是李寻欢
点击五秒禁用,五秒后启用
bind
方法创建新的函数,与原函数的唯一的变化是改变了 this
的值。
call:fun.call(this,arg1, arg2,…)
apply:fun.apply(this, [arg1, arg2,…])
bind:fun.bind(this, arg1, arg2,…)
解构赋值是一种快速为变量赋值的简洁语法,本质上仍然是为变量赋值,分为数组解构、对象解构两大类型。
=
左侧的 []
用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量undefined
...
获取剩余单元值,但只能置于最末位undefined
时默认值才会生效<script>
// 普通的数组
let arr = [1, 2, 3];
// 批量声明变量 a b c
// 同时将数组单元值 1 2 3 依次赋值给变量 a b c
let [a, b, c] = arr;
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
script>
答案是5个9最后一个报错
1.构造函数是专门用于创建对象的函数,如果一个函数使用 new 关键字调用,那么这个函数就是构造函数。
2.使用 new 关键字调用函数的行为被称为实例化
3.实例化构造函数时没有参数时可以省略 ()
4.构造函数的返回值即为新创建的对象
5.构造函数内部的 return 返回的值无效!
6.实践中为了从视觉上区分构造函数和普通函数,习惯将构造函数的首字母大写。
创建对象除了常规的那种,属性还可以放在constructor里面
实例成员:通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员。
this
实际上就是实例对象,为其动态添加的属性和方法即为实例成员constructor
属性指向了构造函数instanceof
用于检测实例对象对应的构造函数<script>
// 构造函数
function Person() {
// 构造函数内部的 this 就是实例对象
// 实例对象中动态添加属性
this.name = '小明';
// 实例对象动态添加方法
this.sayHi = function () {
console.log('大家好~');
}
}
// 实例化,p1 是实例对象
// p1 实际就是 构造函数内部的 this
let p1 = new Person();
console.log(p1);
console.log(p1.name); // 访问实例属性
p1.sayHi(); // 调用实例方法
script>
静态成员:在 JavaScript 中底层函数本质上也是对象类型,因此允许直接为函数动态添加属性或方法,构造函数的属性和方法被称为静态成员
this
指向构造函数本身<script>
// 构造函数
function Person(name, age) {
// 省略实例成员
}
// 静态属性
Person.eyes = 2;
Person.arms = 2;
// 静态方法
Person.walk = function () {
console.log('^_^人都会走路...');
// this 指向 Person
console.log(this.eyes);
}
script>
Object:是内置的构造函数,用于创建普通对象。
Array:是内置的构造函数,用于创建数组。
RegExp:内置的构造函数,用于创建正则表达式
Object
Array
RegExp
在 JavaScript 中的字符串、数值、布尔具有对象的使用特征,如具有属性和方法,之所以具有对象特征的原因是字符串、数值、布尔类型数据是 JavaScript 底层使用 Object 构造函数“包装”来的,被称为包装类型。(MDN网站查找文档)
1.String
取出字符串某个字符,以及trim()
split(‘’)把字符串分割单个字符,如果是c那就分割成以c分割的字符串)
toLowerCase变成小写,toUpperCase都为大写
indexOf(‘字符/字符串’),查找元素第一次出现的位置、lastIndexOf()
slice(start, end)字符串截取(数组的截取是splice(start, length))、substring(start, end)也是截取、substr(start,length)也是
2.Number
<script>
// 使用构造函数创建布尔类型
let locked = new Boolean('10');
// 字面量创建布尔类型
let flag = true;
// 检测是否属于同一个构造函数
console.log(locked.constructor === flag.constructor);
script>
JavaScript 中一切皆为对象,还有以前学习的 window、Math 对象,最后补充一点无论是引用类型或是包装包类型都包含两个公共的方法 toString
和 valueOf
valueOf
方法获取原始值,数据内部运算的基础,很少主动调用该方法toString
方法以字符串形式表示对象<script>
// 对象类型数据
let user = {name: '小明', age: 18}
// 数值类型
let num = 12.345;
// 字符串类型
let str = 'hello world!';
str.valueOf(); // 原始值
user.toString(); // 表示该对象的字符串
script>
实际上每一个构造函数都有一个名为 prototype
的属性,译成中文是原型的意思,prototype
的是对象类据类型,称为构造函数的原型对象,每个原型对象都具有 constructor
属性代表了该原型对象对应的构造函数。
1.实际上prototype的话就可以去增添方法那些,后面实例化的对象都可以使用了,一般普通实例化的对象是不会对prototype的方法属性增添的,
2.还有就是如果实例化的对象自己有那个方法就优先用自己的,否则就使用prototype的)
如果实例化的对象,本身没有prototype,就会通过自身的constructor构造函数去找原型对象prototype
在 JavaScript 对象中包括了一个非标准备的属性 proto 它指向了构造函数的原型对象,通过它可以清楚的查看原型对象的链状结构。
extends
是 ECMAScript 6 中实现继承的简洁语法,是专门用于实现继承的语法关键字,Person
称为父类、Chinese
称为子类。<script>
class Person {
// 父类的属性
legs = 2;
arms = 2;
eyes = 2;
// 父类的方法
walk () {
console.log('人类都会走路...');
}
// 父类的方法
sleep () {
console.log('人都得要睡觉...');
}
}
// Chinese 继承了 Person 的所有特征
class Chinese extends Person {}
// 实例化
let c1 = new Chinese(); //c1也有那些属性和方法
c1.walk();
script>
在继承的过程中子类中 constructor
中必须调 super
函数,否则会有语法错误,子类构造函数中的 super
函数的作用是可以将子类实例化时获得的参数传入父类的构造函数之中。
<script>
class Person {
// 构造函数
constructor (name, age) {
this.name = name;
this.age = age;
}
// 父类的属性
legs = 2;
arms = 2;
walk () {
console.log('人类都会走路...');
}
}
// 子类 English 继承了父类 Person
class English extends Person {
// 子类的构造函数
constructor (name, age) {
super(name, age);
}
// 子类的属性
skin = 'white';
language = '英文';
}
// 实例化
let e1 = new English('jack', 18);
console.log(e1.name);
script>
ECMAScript 6 中基于类的面向对象相较于构造函数和原型对象的面向对象本质上是一样的,基于类的语法更为简洁,未来的 JavaScript 中也都会是基于类的语法实现,当前阶段先熟悉基于类的语法,后面课程中会加强基于类语法的实践。
拷贝不是直接赋值(赋值的对象是改一个另一个改,传的地址)
浅拷贝:
含义:只拷贝最外面层的拷贝方式
let obj = {
uname : '张三丰',
age : 22,
sex : '男',
color : ['red', 'blue', 'yellow', 'pink'],
message : {
index : 1,
score : 99
}
}
let newObj = {};
Object.assign(newObj, obj);
console.log( obj, newObj );
深拷贝: