JavaScript进阶-创新互联
一 面向对象
1 ES6之前的对象
1 基本对象的创建
1 定义一个函数(构造器)对象,使用this定义属性
漳县网站建设公司创新互联建站,漳县网站设计制作,有大型网站制作公司丰富经验。已为漳县近千家提供企业网站建设服务。企业网站搭建\成都外贸网站建设要多少钱,请找那个售后服务好的漳县做网站的公司定做!
2 使用new 和构造器创建一个新对象
当使用new关键字后,其函数的调用将不再是函数,而是一个原型,就以这个原型为模板,构造出自己的实例,然后进行对应的个性化操作,将其当做构造器来处理其特殊的属性或方法。
//古老的定义对象的方式
var obj= {
a:'abc',
b:'123',
c:'[1,2,3,4]',
}
console.log(obj.a,obj.b,obj.c)
function fn(x){
this.x=x;
}
console.log(typeof(fn)) // 此处返回是一个函数
function fn1(x,y) {
this.x=x;
this.y=y;
console.log(this) //指代函数本身,而不是实例化后的结果
console.log(arguments)
}
a=new fn1(10,20) //对象构建,fn1由普通函数变成了一个构造器,new 先做对象,基于原型将a进行处理,通过对象亏帮属性
console.log(a.x,a.y)
结果如下
2 继承关系
function fn(x,y) {
console.log('fn')
this.x=x; //初始化属性
this.y=y;
this.show=()=> ('show') //初始化方法,及函数
}
//继承
function fn1(x,y,z) {
console.log('fn1')
fn.call(this,x,y); //调用父类构造器,
this.z=z;
}
a=new fn1(1,2,3)
console.log('fn1',a) //此处将a修改成一个对象,及字典,其对象中包含着所有的属性和方法
console.log(a.show(),a.x,a.y,a.z)
结果如下
2 ES6之中的对象
1 要求和基本定义处理
1 类的定义使用class 关键字,创建的本质还是函数,是一个特殊的函数
2 一个类只能拥有一个名为constructor的构造方法,如果没有显示的定义一个构造方法,则会默认添加一个constructor方法
3 继承使用extends 关键字
4 一个构造器可以使用super关键字来调用父类的构造函数
5 类没有私有属性
class fn{ //定义类
constructor(x,y){
console.log('fn')
this.x=x; //定义属性
this.y=y;
}
show () //定义方法
{
console.log(this,this.x,this.y)
}
}
//继承关系
class fn1 extends fn{ //使用extends表示继承
constructor(x,y,z){
console.log('fn1')
super(x,y); //调用父类方法,必须传入对应的值,否则父类无入值则会报错。
this.z=z;
}
}
a=new fn1(1,2,3)
console.log(a.show()) //调用父类的方法
console.log(a.x,a.y,a.z)
结果如下
2 函数重载
函数重载及函数名称相同,参数个数或类型不同,此处称为重载
子类汇总直接可以重写父类的方法,如果需要使用父类的方法,则使用super.method()的方式调用
class fn{ //定义类
constructor(x,y){
console.log('fn')
this.x=x; //定义属性
this.y=y;
}
show () //定义方法
{
console.log(this,this.x,this.y)
}
}
class fn1 extends fn{ //使用extends表示继承
constructor(x,y,z){
console.log('fn1')
super(x,y); //调用父类方法,必须传入对应的值,否则父类无入值则会报错。
this.z=z;
}
show(){ //属性重载操作
console.log(this,this.x,this.y,this.z)
}
}
a=new fn1(1,2,3)
console.log(a.show()) //重载后的属性调用
console.log(a.x,a.y,a.z)
结果如下
使用箭头函数重写方法
class fn{ //定义类
constructor(x,y){
console.log('fn')
this.x=x; //定义属性
this.y=y;
}
show = () => console.log(this,this.x,this.y)
}
class fn1 extends fn{ //使用extends表示继承
constructor(x,y,z){
console.log('fn1')
super(x,y); //调用父类方法,必须传入对应的值,否则父类无入值则会报错。
this.z=z;
}
show = () => console.log(this,this.x,this.y,this.z)
}
a=new fn1(1,2,3)
console.log(a.show()) //重载后的属性调用
console.log(a.x,a.y,a.z)
3 类和属性的继承先后次序
class fn{ //定义类
constructor(x,y){
this.x=x; //定义属性
this.y=y;
this.show=() => console.log('this fn',this.x,this.y)
}
show = () => console.log('fn',this.x,this.y)
}
class fn1 extends fn{ //使用extends表示继承
constructor(x,y,z){
super(x,y); //调用父类方法,必须传入对应的值,否则父类无入值则会报错。
this.z=z;
this.show=() => console.log('this fn1',this.x,this.y,this.z);
}
show = () => console.log('fn1',this.x,this.y,this.z);
}
a=new fn1(1,2,3)
console.log(a.show())
结果如下
此处默认调用的是子类的属性,
class fn{ //定义类
constructor(x,y){
this.x=x; //定义属性
this.y=y;
this.show=() => console.log('this fn',this.x,this.y)
}
show = () => console.log('fn',this.x,this.y)
}
class fn1 extends fn{ //使用extends表示继承
constructor(x,y,z){
super(x,y); //调用父类方法,必须传入对应的值,否则父类无入值则会报错。
this.z=z;
// this.show=() => console.log('this fn1',this.x,this.y,this.z);
}
show = () => console.log('fn1',this.x,this.y,this.z);
}
a=new fn1(1,2,3)
console.log(a.show())
结果如下
此处调用的是子类的方法
class fn{ //定义类
constructor(x,y){
this.x=x; //定义属性
this.y=y;
this.show=() => console.log('this fn',this.x,this.y)
}
show = () => console.log('fn',this.x,this.y)
}
class fn1 extends fn{ //使用extends表示继承
constructor(x,y,z){
super(x,y); //调用父类方法,必须传入对应的值,否则父类无入值则会报错。
this.z=z;
// this.show=() => console.log('this fn1',this.x,this.y,this.z);
}
// show = () => console.log('fn1',this.x,this.y,this.z);
}
a=new fn1(1,2,3)
console.log(a.show())
结果如下
class fn{ //定义类
constructor(x,y){
this.x=x; //定义属性
this.y=y;
// this.show=() => console.log('this fn',this.x,this.y)
}
show = () => console.log('fn',this.x,this.y)
}
class fn1 extends fn{ //使用extends表示继承
constructor(x,y,z){
super(x,y); //调用父类方法,必须传入对应的值,否则父类无入值则会报错。
this.z=z;
// this.show=() => console.log('this fn1',this.x,this.y,this.z);
}
// show = () => console.log('fn1',this.x,this.y,this.z);
}
a=new fn1(1,2,3)
console.log(a.show())
结果如下
总结
属性和方法定义,不管是父类还是子类。都是优先使用属性,若两个都是方法,则子类覆盖父类 ,如果都是属性,则是子类覆盖父类,子类的优先级是高于父类的
4 静态方法
静态属性目前支持不完全
在方法前面加上static就是静态方法了,此处的静态方法是在类中进行调用的,而不是在实例中。
class fn{ //定义类
constructor(x,y){
this.x=x; //定义属性
this.y=y;
}
static show = () => console.log('fn',this.x,this.y)
}
console.log(fn.show())
a=new fn(1,2)
console.log(a.show())
结果如下
3 this 中坑
1 问题引出
虽然JavaScript和C和Java都有this,但JavaScript的表现是不同的
原因在于C,Java是静态编译语言,this是在编译期间绑定的,而js是动态语言,是在运行期间锁定的。
var school= { //此处定义一个对象,其中包含name和getNamefunc两个,其中name是属性,getNamefunc是方法,其返回值是一个函数
name: 'test',
getNamefunc: function(){
console.log(this.name)
console.log(this)
console.log('-----------------')
return function() {
console.log(this == global); //检查其是否是全局的
return this.name;
}
}
}
method=school.getNamefunc //此属性调用后返回的是函数的类型,并未调用外层函数
a=method() //调用外层函数
a() //调用内层函数
结果如下
上面的返回结果是其this本应该是school,却被处理称为了全局函数
var school= { //此处定义一个对象,其中包含name和getNamefunc两个,其中name是属性,getNamefunc是方法,其返回值是一个函数
name: 'test',
getNamefunc: function(){
console.log(this.name)
console.log(this)
console.log('-----------------')
return function() {
console.log(this == global); //检查其是否是全局的
return this.name;
}
}
}
method=school.getNamefunc() //此处调用外层函数
a=method() //调用外层函数
结果如下
此处的结果是this仍然是全局属性
分析上例
第三行的打印结果是true,则表明当前是global的作用于,因为调用这个返回的函数是直接调用的,这是一个普通函数调用,因此this是全局对象第四行undefined,就是因为this是global,所以没name属性
这就是函数调用的时候,调用方式不同,this对应的对象不同,它已经不是C++,Java的指向实例本身了。
this的问题,这是历史遗留问题,新版本只能兼容了。
2 解决办法
1 显式传参
var school= { //此处定义一个对象,其中包含name和getNamefunc两个,其中name是属性,getNamefunc是方法,其返回值是一个函数
name: 'test',
getNamefunc: function(){
console.log(this.name)
console.log(this)
console.log('-----------------')
return function(that) { //通过此处传递参数的方式改变
console.log(that == global); //检查其是否是全局的
return that.name;
}
}
}
console.log(school.getNamefunc()(school))
结果如下
2 ES3 引入了apply和 call方法
通过call将指定的this进行指定了。JavaScript中指代的是这个,其他的Java指定的不同,普通函数的调用和对象的调用是不同的,普通函数的调用中的thi指代的是全局变量,而在对象中调用,默认传递的值是this的本身
var school= { //此处定义一个对象,其中包含name和getNamefunc两个,其中name是属性,getNamefunc是方法,其返回值是一个函数
name: 'test',
getNamefunc: function(){
console.log(this.name)
console.log(this)
console.log('-----------------')
return function() {
console.log(this == global); //检查其是否是全局的
return this.name;
}
}
}
var school1={
'name': 'test1'
}
method=school.getNamefunc() //此属性调用后返回的是函数的类型,并未调用外层函数
console.log(method.call(school1)) //通过call将其this传递进去,
console.log('---------------')
console.log(method.call(school))
console.log('++++++++++++++++')
console.log(method.apply(school)) //通过apply,将其this传递过去
结果如下
相关源码
apply和 call方法都是函数对象的方法
apply传递其他参数使用数组,call传递其他参数需要使用可变参数收集。相关情况如下
var school= { //此处定义一个对象,其中包含name和getNamefunc两个,其中name是属性,getNamefunc是方法,其返回值是一个函数
name: 'test',
getNamefunc: function(){
console.log(this.name)
console.log(this)
console.log('-----------------')
return function(x,y,z) {
console.log(this == global,x,y,z); //检查其是否是全局的
return this.name;
}
}
}
method=school.getNamefunc() //此属性调用后返回的是函数的类型,并未调用外层函数
console.log('---------------')
console.log(method.call(school,[1,2,3])) //此处传地过去不会解构,是一个参数
console.log('++++++++++++++++')
console.log(method.apply(school,[1,2,3])) //通过apply,会解构,
结果如下
3 ES5中引入了bind方法
var school= { //此处定义一个对象,其中包含name和getNamefunc两个,其中name是属性,getNamefunc是方法,其返回值是一个函数
name: 'test',
getNamefunc: function(){
console.log(this.name)
console.log(this)
console.log('-----------------')
return function(x,y,z) {
console.log(this == global,x,y,z); //检查其是否是全局的
return this.name;
}
}
}
method=school.getNamefunc() //此属性调用后返回的是函数的类型,并未调用外层函数
console.log(method.bind(school)(1,2,3))
结果如下
bind 是会返回一个新函数,其和偏函数相似
4 ES6中支持了this箭头函数
var school= { //此处定义一个对象,其中包含name和getNamefunc两个,其中name是属性,getNamefunc是方法,其返回值是一个函数
name: 'test',
getNamefunc: function(){
console.log(this.name)
console.log(this)
console.log('-----------------')
return (x,y,z) => {
console.log(this == global,x,y,z); //检查其是否是全局的
return this.name;
}
}
}
school1={
name:'test1'
}
method=school.getNamefunc() //此属性调用后返回的是函数的类型,并未调用外层函数
console.log(method(1,2,3))
结果如下
最外层使用箭头函数的结果
var school= { //此处定义一个对象,其中包含name和getNamefunc两个,其中name是属性,getNamefunc是方法,其返回值是一个函数
name: 'test',
getNamefunc: () => {
console.log(this.name)
console.log(this)
console.log('-----------------')
return (x,y,z) => {
console.log(this == global,x,y,z); //检查其是否是全局的
return this.name;
}
}
}
school1={
name:'test1'
}
method=school.getNamefunc() //此属性调用后返回的是函数的类型,并未调用外层函数
console.log(method(1,2,3))
结果如下
因此,一般不建议最外层使用箭头函数
4 高阶对象
1 简介
Mixin 模式,及混合模式,这是一种不用继承就能复用的技术,主要还是为了解决多重继承的问题,多重继承路径是个问题
JS是基于对象的,类和对象都是对象模板
混合Mixin,指的是将一个对象的全部或者部分拷贝到另一个对象上去了,其实就是属性,可以将多个类或者对象混合成一个类或者对象,任何一个对象都可以作为另一个对象的原型。
2 基础代码
class test{
constructor(){
console.log('test',this)
if (typeof(this.show) !=='function') {
throw new ReferenceError('should define stringify.');
}
}
}
class test1 extends test{
constructor(x,y){
super();
this.x=x;
this.y=y;
}
show(){
return `test1 ${this.x} ${this.y}`;
}
}
p=new test1(1,2)
console.log(p.show())
结果如下
此处的核心是谁调用的问题,上述父类的this是子类的实例,而不是子类调用父类后父类形成的实例,因此这也是验证通过的原因。
class test{
constructor(){
console.log('test',this)
if (typeof(this.show) !=='function') {
throw new ReferenceError('should define stringify.');
}
}
}
class test1 extends test{
constructor(x,y){
super();
this.x=x;
this.y=y;
}
show(){
return `test1 ${this.x} ${this.y}`;
}
}
class test2 extends test1 {
constructor(x,y,z) {
super(x,y);
this.z=z;
}
}
p=new test2(1,2)
console.log(p.show()) //此处自己本身没有show,则通过调用父类的show来实现,上述传入的this 仍然是子类的实例,
结果如下
3 高阶对象实现
通过在test中使用函数并传递参数的方式将test1的相关属性注入进去,相关代码如下
初步改造结果如下
class test1{
constructor(x,y){
this.x=x;
this.y=y;
}
show(){
return `test1 ${this.x} ${this.y}`;
}
}
const test = function test(SUP) { //此函数的返回值是一个类,通过将类test1传入其中进行相关的操作处理并进行输出
return class extends SUP {
constructor(...args) {
super(...args);
console.log('test',this)
if (typeof(this.show) !=='function') {
throw new ReferenceError('should define stringify.');
}
}
}
}
class test2 extends test(test1){
constructor(x,y,z) {
super(x,y);
this.z=z;
}
}
p=new test2(1,2,3)
console.log(p.show())
结果如下
将上述函数修改为箭头函数结果如下
class test1{
constructor(x,y){
this.x=x;
this.y=y;
}
show(){
return `test1 ${this.x} ${this.y}`;
}
}
const test = SUP=> class extends SUP {
constructor(...args) {
super(...args);
console.log('test',this)
if (typeof(this.show) !=='function') {
throw new ReferenceError('should define stringify.');
}
}
}
class test2 extends test(test1){
constructor(x,y,z) {
super(x,y);
this.z=z;
}
}
p=new test2(1,2,3)
console.log(p.show())
注意:
test(test1)这一步实际上是一个匿名箭头函数的调用,返回一个新的类型,test2继承自这个新的类型,增强了功能,react大量使用了这种Mixin技术。
二 异常
1 基础实例
try {
throw 1; // 抛出异常
} catch (error){ //此处不用指定异常类型,此处的error是上面的throw扔出来的
console.log(error,typeof(error));
}
抛出一个对象
try {
throw {}; // 抛出异常
} catch (error){ //此处不用指定异常类型,此处的error是上面的throw扔出来的
console.log(error,typeof(error));
}
结果如下
抛出一个函数
try {
throw () => console.log(2); // 抛出异常
} catch (error){ //此处不用指定异常类型,此处的error是上面的throw扔出来的
console.log(error,typeof(error));
}
结果如下
2 抛出异常
try {
throw Error('new Error'); // 抛出异常
} catch (error){ //此处不用指定异常类型,此处的error是上面的throw扔出来的
console.log(error,typeof(error));
}
结果如下
try {
throw ReferenceError('new Error')
} catch (error){ //此处不用指定类型 ,此处的error是接上面扔出来的东西
console.log(error,typeof(error));
};
结果如下
try {
throw ReferenceError('new Error')
} catch (error){ //此处不用指定类型 ,此处的error是接上面扔出来的东西
console.log(error,typeof(error)); //此处不影响下面的执行
console.log(error.constructor); //此处的执行结果是函数
};
结果如下
try {
throw ReferenceError('new Error')
} catch (error){ //此处不用指定类型 ,此处的error是接上面扔出来的东西
console.log(error,typeof(error)); //此处不影响下面的执行
console.log(error.constructor.name); //获取函数名称
};
结果如下
3 必须执行的finally
try {
throw null;
} catch (error){ //此处不用指定类型 ,此处的error是接上面扔出来的东西
console.log(error,typeof(error));
console.log(error.constructor.name); //一切皆函数
} finally { //定义必须执行的函数
console.log('1325345234')
}
结果如下
三 解构和相关方法
1 JS数组解构
1 常量的修改
常量的声明和初始化时不能被分开的,常量只是对象的地址不能发生改变了,并不是其中的内容也不能改变了。
const l1=[1,2,3,4]
l1.pop() //l1虽然是一个常量,但其可以被修改
console.log(l1)
2 参数解构
完全解构
const l1=[1,2,3,4]
const [a,b,c,d]=l1
console.log(a,b,c,d)
丢弃解构
const l1=[1,2,3,4]
const [a,b,,c,d]=l1
console.log(a,b,c,d)
结果如下
const l1=[1,2,3,4]
const [a]=l1
console.log(a)
结果如下
可变变量
const l1=[1,2,3,4]
const [a,...d]=l1
console.log(a,d)
结果如下
const l1=[1,2,3,4]
const [a,...d,e]=l1 //可变长必须放置在最后面
console.log(a,d,e)
默认值
const l1=[1,2,3,4]
const [x=10,,,,y=20]=l1 //能覆盖的覆盖,不能覆盖的保持原值,先赋值,再覆盖,
console.log(x,y)
3 复杂数组解构
const arr = [1,[2,3],4]; //三个元素,嵌套解构
const [a,[b,c],d]=arr;
console.log(a,b,c,d); //直接拆开
const [e,f]=arr; // 只有两
console.log(e,f); // 第一个和第二个
const [g,h,i,j=18]=arr; // 三个 加1 ,则是对应的
console.log(g,h,i,j);
const [k,...l]=arr; // 此处会将后面两个元素进行搬家
console.log(k,l);
结果如下
2 数组方法
1 方法描述
方法 | 描述 |
---|---|
push(...items) | 尾部追多个元素 |
pop() | 移除最后一个元素,并返回它 |
map | 引入处理函数来处理数组中的每一个元素,返回新的数组 |
filter | 引入处理函数处理数组中的每一个元素,此处理函数返回true的元素保留,否则该元素被过滤掉,保留的元素构成新的数组返回 |
foreach | 迭代所有元素,无返回值 |
2 push方法
const l1=[1,2,3,4]
l1.push([5,6,7,8])
l1.push(...['a','b','c'])
l1.push(9,10,11)
console.log(l1)
结果如下
3 pop 方法
const l1=[1,2,3,4]
l1.push([5,6,7,8])
l1.push(...['a','b','c'])
l1.push(9,10,11)
console.log(l1.pop())
console.log(l1.pop())
console.log(l1.pop())
console.log(l1)
结果如下
4 map方法
const abs = (x) => x**2
const l1=[1,2,3,4]
console.log(l1.map(abs))
结果如下
5 filter
const abs = function (x) {
if (x%2==0) {
return true;
}
else {
return false;
}
}
const l1=[1,2,3,4]
console.log(l1.filter(abs))
结果如下
6 foreach
const l1=[1,2,3,4]
const newarr= l1.forEach(x=>x+10) //无返回值
console.log(newarr,l1)
结果如下
7 数组练习
有一个数组const arr=[1,2,3,4,5];,要求计算出所有元素平方值是偶数且大于10的。
代码一
const arr=[1,2,3,4,5];
console.log(arr.filter(x => ((x**2)%2==0 && (x**2)>10)))
代码二,若平方是偶数,则该数也应该是偶数,则如下
const arr=[1,2,3,4,5];
console.log(arr.filter((x=> x%2==0 && x**2>10)))
代码三,通过map来实现
const arr=[1,2,3,4,5];
console.log(Math.log2(((arr.filter(x=>x%2==0)).map(x=>x**2)).filter(x=>x>10)))
代码四,求10的平方根,然后再进行相关比较
const arr=[1,2,3,4,5];
const cond=10
const cond1=Math.sqrt(cond)
console.log(arr.filter(x=>x%2==0 && x> cond1))
3 对象解构
var metadata= {
title: "Seratchpad",
translations :[
{
locale: "de",
localization_tags: [],
last_edit: "2019-01-01T08:11:11",
url: "/de/docs/Tools/scratchpad",
title: "JavaScript-Umgebung"
}
],
url: "/en-US/docs/Tools/Scratchpad"
}
var {title:enTitle,translations:[{title: localeTitle}] }=metadata; //: 后面的是重命名
console.log(enTitle)
console.log(localeTitle)
4 对象操作
1 基本介绍
object 的静态方法 | 描述 |
---|---|
object.keys(obj) | ES5开始支持,返回所有key |
object.values(obj) | 返回所有值,实验阶段,支持较差 |
object.entries(obj) | 返回所有值,试验阶段,支持较差 |
object.assign(target,...sources) | 使用多个source对象,来填充target对象,返回target对象 |
var obj = {
a:100,
b:200,
c:()=>{}
}
console.log(Object.keys(obj)) //返回所有的键
console.log(Object.values(obj)) //返回所有的值
console.log(Object.entries(obj))//返回所有的键和值
obj1={
d:300,
e:400,
}
let obj2=Object.assign(obj,obj1) //返回新的对象
console.log()
结果如下
四 模块化
1 简介
ES6 之前,JS没有出现模块化系统
JS主要是在前端浏览器中使用,JS文件下载缓存到客户端,在浏览器中执行。
比如简单的表达本地验证,漂浮一个广告。
服务器端使用ASP,JSP等动态网页技术,将动态生成数据嵌入一个HTNL模板,里面夹杂着JS后使用