自由变量:函数内部使用外部的变量,这个变量就是自由变量。
注:跨域的变量都是自由变量,即变量声明和使用不在一个作用域。
递归:
定义:自己调用自己。
注:一定要有结束(出口)。
所有的编程语言通过编程范式分为:
命令式编程——分为面向过程式编程和面向对象式编程
声明式编程
自动拆箱,自动装箱 内置对象(包装类):
创建构造函数:和普通函数的书写方式一样(命名以大驼峰方式)
基本数据类型:字符串可以直接使用String的方法和属性
基本数据类型:数值不可以直接使用toString的方法
原因:数值分整型和浮点型(小数)
123.toString() 123后面的点,会被识别为小数点,不会装箱为对象
解决方式为:
(123).toString();
自动拆箱(new后面跟的就是拆开后的数据类型)
let num = new String(456);
num.abc = 123;
console.log(num.abc, typeof num.abc);//123 ‘number’
console.log(num - 1);//455
console.log(num + 1);//4561
面向对象(Oriented Object): (Analysis Design分析设计JAVA)
面向过程:把解决问的方法和步骤,一步一步写出来(JS代码)。
面向对象:把解决问的方法和步骤中涉及到的属性和方法,封装成一个构造函数(对象)。
类(class):具有相同的属性和行为的一组对象的抽象,既对于一类对象的抽象描述;
使用时,就像是一类事物的生产模板,可以用类来产出任意数量的实际对象。
类与对象的关系:
类是对象的抽象,对象是类的实例。
// let f71 = new f70();
// console.log(f71);
// let num = 123;
// console.log(num,num.length,typeof num);
let f70 = num.toString();
console.log(f70,typeof f70);
基本数据类型:字符串可以直接使用String的方法和属性
基本数据类型:数值不可以直接使用toString的方法
原因:数值分整型和浮点型(小数)
//自动拆箱(new后面跟的就是拆开后的数据类型)
let num = new String(456);
num.abc = 123;
console.log(num.abc, typeof num.abc);//123 ‘number’
console.log(num - 1);//455
console.log(num + 1);//4561
构造函数:
先创建一个构造函数(命名以大驼峰命名法)
函数内自动会创建一个叫this的对象
函数内的实参和形参的作用就是赋值(中间以=来赋值,如:this.name = name;)
如果没有return,函数内会自动补充一个return,返回的是this这个对象
如果返回的是基本数据类型,不会影响,继续返回this这个对象;如果返回的是引用数据类型,则返回的是这个引用数据
调用构造函数,就是传参数
格式:let stu1 = new Students(“xiaoguo”,18,“male”,“50kg”);(传的参数要跟上面的值一一对应)
function Students(name,age,gender,weight){
// let this = {}
this.name = name;
this.age = age;
this.gender = gender;
this.weight = weight;
// return this; //默认
// return [123]; //有效
// return 123; //无效
}
let stu = new Students();
new 一个实例
1.this的空对象
2.this的"proto"指向"类.prototype"
3.执行代码(给this添加属性和方法)
4.ruturn this(即返回对象给实例)
注:手动更改返回值,更改基本数据类型无效。更改引用值有效。
ES5中打印类,则会把所有的function里的东西打印出来;而ES6中方法不会被打印出来,但调用的时候也可以使用,这是代码进阶了,不需要占空间导致卡顿。
ES6 类 语法糖:ES6的书写方式
注:类似封装一个构造函数
class Students {
//constructor 构造器
constructor(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
sayhello() {
console.log("hello", this.name);
}
}
let stu = new Students("z3", 18, 180);
console.log(stu.name);
stu.sayhello();
stu.haha();
//prototype建立了一个公有的属性,不需要占空间,但是能被调用到数据。
Students.prototype.banjiName = “f70”;
let stu = new Students(“xiaomimi”, 18, “male”);
let stu1 = new Students(“xiaomim”, 18, “male”);
let stu2 = new Students(“xiaomi”, 18, “male”);
stu.rich = “true”;
// 在调用时,如果没有__proto__属性,则后面再调用时获取不了值
console.log(stu.rich); //true
console.log(stu1.rich); //un
console.log(stu2.rich); //un
// 如果有__proto__属性,则只要调用构造函数的所有都可以调用这个属性,在当前构造函数中创建了一个公有的区域
stu.proto.rich = “true”;
console.log(stu.rich); //true
console.log(stu1.rich); //true
console.log(stu2.rich); //true
// prototype和__proto__建立的是同一个公有区域,只是前面调用的方式不一样
let stu4 = new Students();
// ES5可以使用普通函数调用方式,ES6会报错,提示调用前加new
let stu5 = Students();
/*
私有:
this.xxx = xxx
this.xxx = function (){}
公有:
类.prototype.xxx = xxx
类.prototype…xxx = function (){}
实例.proto.xxx = xxx
实例.proto…xxx = function (){}
注:同类别的实例对象可以使用公有的属性和方法
*/
typeof 类 ——function
注:类就是函数
普通函数和构造函数本质区别:调用方式
1.普通函数: 函数名()
2.构造函数: new 函数()
3.类: new 类名()
[this指向]:
1.普通对象,谁调用就指向谁
2.构造函数中的this,指向的是实例化对象
3.监听器中的this,指向的是绑定事件的节点
4.普通函数中的this,指向的是window,var声明可以把window内的值更改
注:严格模式下(在函数第一行如果有"use strict",即为严格模式),var声明也无效,都指向的是undefined
5.箭头函数中this的指向,取决于所处环境;箭头函数没有自己的arguments和this
书写例子:
document.addEventListener("click", function () {
new Qiu(shuZi(100,200), `rgba(${shuZi(0,255)}, ${shuZi(0,255)}, ${shuZi(0,255)}, 0.5)`, shuZi(0,500), shuZi(0,1200));
})
let divEle = document.getElementsByClassName("lol")[0];
class Qiu {
constructor(width,bgc, top, left) {
this.width = width;
this.height = width;
this.backgroundColor = bgc;
this.top = top;
this.left = left;
this.node = document.createElement("div");
this.init();
}
init(){
this.show();
}
show() {
// v1.0
/* this.node.style.width = this.width + "px";
this.node.style.height = this.height + "px";
this.node.style.backgroundColor = this.backgroundColor;
this.node.style.position = "absolute";
this.node.style.borderRadius = "50%";
this.node.style.top= this.top + "px";
this.node.style.left = this.left + "px";
divEle.appendChild(this.node) */
// v2.0 简写方式
Object.assign(this.node.style,{
width:this.width+"px",
height:this.height+"px",
backgroundColor:this.backgroundColor,
top:this.top+"px",
left:this.left+"px",
position:"absolute",
borderRadius:"50%",
},divEle.appendChild(this.node))
}
}
改变this的指向:
例子:
function f70(){
this.name ="a";
this.say = function(weight,height){
console.log(this.name,weight,height);
}
}
let stu1 = new f70;
let stu2 = {
name:"xiaoguo"
}
stu1.say();//a undefined undefined
// 改变this的指向
// 类名.方法名.call(this指向的地方,"第一个参数","第n个参数");
stu1.say.call(stu2,"60kg","175");// xiaoguo 60kg 175
// 类名.方法名.apply(this指向的地方,["第一个参数","第n个参数"]);//参数需要以数组的方式传输
stu1.say.apply(stu2,["70kg","180"]);//zhixiang.js:4 xiaoguo 70kg 180
// 类名.方法名.bind(this指向的地方,"第一个参数","第n个参数");//返回是一个函数;需要接收,然后调用,才执行
let xiaoguoH=stu1.say.bind(stu2,"80kg","190");// xiaoguo 80kg 190
xiaoguoH();
原型链:
let fn = new Function();
执行时,{如果fn.方法名,如果本身没有方法—>原型链—>{}也是Object—>null}
如果以上都没有则会报错
每个函数都有一个prototype属性叫原型
console.log(Function.prototype); //{}
console.log(fn.prototype); //constructor
fn.__proto__;//原型链
fn.__proto__.__proto__;//{}
fn.__proto__.__proto__.__proto__;//null
原型链:
1、找自身的属性,如果没有做第2步
2、在原型找属性,如果没有做第3步
3、在原型的__proto__找属性
封装、继承、多态[面向对象的三大特征]:
1.【封装】
ES5 构造函数添加属性和方法
function F70(name) {
this.name = name;
}
F70.prototype.say = function() {
console.log(this.name);
}
ES6 属性和方法封装成一个对象
class F70 {
constructor(name) {
this.name = name
}
say() {
console.log(this.name);
}
}
2.【继承】inherit
ES5:
function Father(firstName, lastName, money) {
this.lastName = lastName;
this.firstName = firstName;
this.money = money;
}
let F1 = new Father(1, 2, 100);
console.log(F1.money);//100
Son.prototype.gender = true;
Object.assign(Son.prototype, F1)
function Son(age) {
this.age = age;
}
let S1 = new Son(80);
console.log(S1.age);//80
console.log(S1.money);//100
console.log(S1.gender);//true
ES6:
class Father {
constructor(name, age, money) {
this.name = name;
this.age = age;
this.money = money;
}
}
//继承就是在class Son后添加需要被继承的函数名称extends Father
class Son extends Father {
constructor(name, age, money) {
//如果要继承,要把需要继承函数中的所有参数都写出来;super必须写在this之前
super(name,age,money);
this.name = name;
this.age = age;
}
}
let S1 = new Son("z3", 12, 100);
console.log(S1.money);//100
上方ES6中实现继承转换成ES5解析步骤:
function GraFather() {
this.house = 998;
}
GraFather.prototype.tiao = function () {
console.log("haihai");
}
function Father() {
this.age = 18;
this.house1 = GraFather;
this.house1();
delete this.house1;
}
Father.prototype.say = function () {
console.log("hello");
}
function Son() {
//在函数建立时,会创建一个this{},此时就会在this{}中添加一个this.age1=Father;
//但是在代码中有跟Father重名的构造函数,所以Father就是一个函数,当执行后,就会
//执行当前函数的内容,如果其中有this,那么this的指向也是当前Son的实例化对象,执行
//完后,我们就需要把当前的this删除,这样就实现了继承。
this.age1 = Father;
this.age1();
delete this.age1;
this.gender = true;
}
let F1 = new Son();
//本来在Son构造函数中没有say方法,就会指向Son的上面的原型链,如果原型链中没有就
//指向上面{}(Object)中是否有say方法,但执行当前步骤后,把son指向上面的原型链,
//原型链指向上面的{}的指向改成了Father的原型链,如果Father的原型链中也没有此方
//法,才找到Father的原型链指向上面的{},如果都没有才报错;如果有,则执行此方法。
Son.prototype.__proto__ = Father.prototype;
Father.prototype.__proto__ = GraFather.prototype;
F1.say();//hello
F1.tiao();//haihai
console.log(F1.age);//18
console.log(F1.house);//998
3.【多态】
简单概况:(同一个模具,生产出不同的产品)
同一个构造函数或类,实例化出不同的对象(根据参数不同)
不同的实例对象,调用同一方法,得到不同的结果
class Students {
constructor(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
hello(place){
console.log(this.name,place)
}
}
let stu1 = new Students("z3", 17, "male");
let stu2 = new Students("l4", 18, "female");
let stu3 = new Students("w5", 19, "male");
stu1.hello("cd");//"z3","cd"
stu2.hello("cc");//"l4","cc"
stu3.hello("cb");//"w5","cb"
【补充内容:扩展】
let obj = {
name: "z3"
};
obj.age = 123;
Object.defineProperty(obj, "gender", {
value: "male",//给添加的属性gender添加内容
writable:true,//可修改内容(默认值false)
enumerable:true,//可改变、可遍历(默认值false)
});
console.log(obj);
obj.gender = "female";
console.log(obj);
// for(let item of obj){
// console.log(item);//会出现报错(枚举),因为它不是数组
// }
for(let item of Object.keys(obj)){
console.log(item);//name,age,gender
}
两种方法只能选择一种
let obj = {
name: "z3"
};
let age = 0;
Object.defineProperty(obj, "cc", {
// 属性被赋值时或更改,触发set方法
// 属性被使用时,触发get方法
//value是用户给的值
set(value){
if(value>40){
age = 19;
}else{
age = 18;
}
},
get(){
return age;//把结果反馈给cc
}
});
console.log(obj.cc);//0
obj.cc = 45;
console.log(obj.cc);//18
obj.cc = 35;
console.log(obj.cc);//19
【深拷贝、浅拷贝】
…的功能:
功能1拆分:批量修改和赋值()
功能2整合:不定参数
浅拷贝:共用一个地址,因此其中一个更改,另一个也会发生改变。
数组版:
let arr = [1,2,3];
let arr2 = arr;//把地址给arr2
arr[2] = "a";
console.log(arr2);//[1,2,"a"]
console.log(arr);//[1,2,"a"]
对象版:
let obj1 = {
name:1,
age:2
}
let obj2 = obj1;
obj1.name = "a";
console.log(obj2.name);//a
深拷贝:不是一个地址,因此其中一个更改,另一个不会发生改变。
数组版:
let arr = [1,2,3];
let arr2 = arr.slice(0);——let arr2 = [...arr];//返回一个新的数组,地址更改了
arr[2] = "a";
console.log(arr2);//[1,2,3]
console.log(arr);//[1,2,"a"]
对象版:
let obj1 = {
name:1,
age:2
}
let obj2 = {...obj1};
obj1.name = "a";
console.log(obj2.name);//1
H5 API应用进程(程序)接口:
A:Application应用;P:Process进程(程序);I:Interface接口;
网页视频的功能:
let v1 = document.getElementsByTagName("video")[0];
let divEle = document.getElementsByClassName("contaniu")[0];
let divEle1 = document.createElement("div");
let index = false;
divEle.addEventListener("click", function (e) {
let event = e || window.event;
let divEle2 = document.getElementsByClassName("guangGao")[0];
if (event.target.value == "播放") {
// play是设置视频播放
v1.play();
if(index){
divEle2.remove();
}
index = false;
} else if (event.target.value == "暂停") {
// pause是设置视频暂停播放
v1.pause();
divEle1.setAttribute("class", "guangGao");
divEle1.innerHTML = `
这是一个小广告
<span class="sc">x</span>
`;
divEle.appendChild(divEle1);
index = true;
} else if (event.target.className == "sc") {
divEle2.remove();
index = false;
} else if (event.target.value == "音量") {
// volume是设置视频播放声音(取值0-1,0是静音,1是最大音量)
v1.volume = 0.3;
} else if (event.target.value == "快进5s") {
//currentTime是时间的设置(快进、后退)
v1.currentTime += 5;
} else if (event.target.value == "后退5s") {
v1.currentTime -= 5;
} else if (event.target.value == "0.2倍播放") {
// playbackRate是倍数播放(0.1-16 —— 1是普通速度,小于1是慢放,大于1是快放)
v1.playbackRate = 0.2;
} else if (event.target.value == "1.5倍播放") {
v1.playbackRate = 1.5;
} else if (event.target.value == "1倍播放") {
v1.playbackRate = 1.5;
} else if (event.target.value == "全屏") {
// 全屏
launchFullscreen(v1);
// v1.webkitRequestFullscreen();谷歌浏览器全屏
}
})
//解决浏览器兼容性
function launchFullscreen(element) {
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullscreen();
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen();
}
}
拖拽:
// 源对象
let divEle = document.getElementsByClassName("sou")[0];
// 获取鼠标在源对象中的位置
let sX, sY;
divEle.addEventListener("dragstart", function (e) {
let event = e || window.event;
// console.log("dragstart");
// 获取当前鼠标在节点的位置
sX = event.offsetX;
sY = event.offsetY;
})
divEle.addEventListener("drag", function (e) {
console.log("drag");
})
divEle.addEventListener("dragend", function (e) {
console.log("dragend");
})
// 目标对象
let divEle1 = document.getElementsByClassName("tou")[0];
// 监听鼠标是否移入
divEle1.addEventListener("dragenter", function () {
console.log("dragenter");
})
// 监听源目标是否在目标对象之上(一直触发)
divEle1.addEventListener("dragover", function (e) {
let event = e || window.event;
event.preventDefault();
})
// 监听鼠标是否移出
divEle1.addEventListener("dragleave", function (e) {
console.log("dragleave");
})
// 监听鼠标是否移出(是否触发,dragover中是否阻止默认事件)
divEle1.addEventListener("drop", function (e) {
let event = e || window.event;
let taX = this.offsetLeft;
let taY = this.offsetTop;
divEle.style.left = `${event.clientX-sX-taX }`+"px";
divEle.style.top = `${event.clientY-sX-taY}`+"px";
this.appendChild(divEle);
})
本地存储:
cookie:只能存储在当前页面,每次存储的数据不能大于4kb
session(会话)
sessionStorage存储:
sessionStorage中存储的键值对都是以string类型存储的
缺点:如果更换了根目录,则存储的数据消失
// 设置数据(增添、修改)
sessionStorage.setItem("className", "f70");
// 获取数据
let cn = sessionStorage.getItem("className");
// console.log(cn); //f70
// 删除数据
sessionStorage.removeItem("className");
cn = sessionStorage.getItem("className");
// console.log(cn); //null
//清空sessionStorage里的数据
sessionStorage.setItem("username", "f71");
sessionStorage.setItem("pwd", 123);
sessionStorage.setItem("balance", 1000);
sessionStorage.clear();
let obj = {
name: "abc",
age: 18,
say() {
console.log("hello");
}
}
// JSON.stringify()把JS中各类型数据转换成"字符串"进行存储。
sessionStorage.setItem("obj", JSON.stringify(obj));
console.log(typeof sessionStorage.getItem("obj")); //string
// 把sessionStorage获取到的值,转换成原本的数据类型
let info = sessionStorage.getItem("obj");
obj = JSON.parse(info);
console.log(typeof obj,obj);//object,{age:18,name:"abc"}
localStorage 永久存储
sessionStorage的方法均可使用
sessionStorage与localStorage的区别:
sessionStorage如果在使用当前页面的值,更改值后,而第二个相同地址中页面的值不会更改;
当关闭页面后,存储的值都会消失。
localStorage如果在使用当前页面的值,更改值后,而第二个相同地址中页面的值会更改;
当关闭页面后,同一个地址下,存储的值不会消失。