ES5、ES6中的参数默认值

2019-11-5 张宇童 学习日志

在ES5中设置默认值是十分麻烦的一件事情

大家看一下,了解就好


function add(x, y, z) {
    if (y === undefined) {
        y = 12
    }
    if (z === undefined) {
        z = 20
    }
    return x + y + z;
}
add(1) // return 33


大家再来看ES6中的默认值处理方式

function add (x, y = 10, z = 20) {
    return x + y + z
}
add(3) // reutrn 33

标签: javascript ES5

评论(0) 浏览(652)

深入理解JavaScript中的class

2019-11-5 张宇童 学习日志

Class

Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象。但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类)。

这是摘自阮一峰老师的博客,这句话放在 ES5 可以说不为过,然而到了 ES6 这么说就已经不严谨了。因为 ES6 中已经有了专属的 class 语法了。

有的同学喜欢函数式的编程方式,有的同学喜欢面向对象的编程思维,我们今天不论长短,重点讲述怎么使用 ES6 的 class 语法完成面向对象的开发。

Intro

对于面向对象编程而言,更关注类的声明、属性、方法、静态方法、继承、多态、私有属性。

Basic Syntax

首先我们要先来说明在 JavaScript 世界里如何声明一个 “类”。在 ES6 之前大家都是这么做的:

let Animal = function (type) { 
    this.type = type 
    this.walk = function () { 
        console.log(`I am walking`) 
    } 
} 
let dog = new Animal('dog')
let monkey = new Animal('monkey')

在上述代码中,我们定义了一个叫 Animal 的类,类中声明了一个属性 type、一个方法 walk;然后通过 new Animal 这个类生成实例,完成了类的定义和实例化。当然你也可以这样写:

let Animal = function (type) {
  this.type = type
}

Animal.prototype.walk = function () {
  console.log(`I am walking`)
}

let dog = new Animal('dog')
let monkey = new Animal('monkey')

在 ES6 中把类的声明专业化了,不在用 function 的方式了,请看:

class Animal {
  constructor (type) {
    this.type = type
  }
  walk () {
    console.log(`I am walking`)
  }
}
let dog = new Animal('dog')
let monkey = new Animal('monkey')

很明显,从定义上就很专业了,有构造函数、方法,但是 ES6 增加了新的数据类型 class 吗?

console.log(typeof Animal); //function

可以发现 class 的类型还是 function,和 ES5 貌似并没有什么区别,那么 class 中定义的方法在哪呢?我们知道只要是函数,就一定会有 prototype 对象。那么类的方法和 prototype 对象有什么关系呢?

console.log(Animal.prototype);
// {constructor: ƒ, walk: ƒ}
//   constructor: class Animal
//   walk: ƒ walk()
//   __proto__:
//   constructor: ƒ Object()
//   hasOwnProperty: ƒ hasOwnProperty()
//   isPrototypeOf: ƒ isPrototypeOf()
//   propertyIsEnumerable: ƒ propertyIsEnumerable()
//   toLocaleString: ƒ toLocaleString()
//   toString: ƒ toString()
//   valueOf: ƒ valueOf()
//   __defineGetter__: ƒ __defineGetter__()
//   __defineSetter__: ƒ __defineSetter__()
//   __lookupGetter__: ƒ __lookupGetter__()
//   __lookupSetter__: ƒ __lookupSetter__()
//   get __proto__: ƒ __proto__()
//   set __proto__: ƒ __proto__()

可以看出在 Animal.prototype 对象上有两个方法,一个是构造函数(constructor)、一个是自定义的方法(walk)。这是不是和 ES5 的第二种写法一模一样?我们再来看下属性,在 ES5 中有个 API 用来判断对象的自有属性(hasOwnProperty)。

console.log(dog.hasOwnProperty('type')); //true

这个表现也和 ES5 中直接使用 function 定义类的方式相同,所以得出一个结论:class 的方式是 function 方式的语法糖。

Setters & Getters

对于类中的属性,可以直接在 constructor 中通过 this 直接定义,还可以直接在类的顶层来定义:

class Animal {
  constructor (type, age) {
    this.type = type
    this._age = age
  }
  get age () {
    return this._age
  }
  set age (val) {
    this._age = val
  }
}

这个代码演示了,通过 get/set 来给类定一个属性,不过貌似没有说服力。因为 age 和 _age 都是类的属性,而且是相同的含义这样做感觉没有实际用途。但是如果一个属性是个只读的呢?

class Animal {
  constructor (type) {
    this.type = type
  }
  get addr () {
    return '北京动物园'
  }
}

毋庸赘述,大家都能看出来含义。再来看下如下的应用场景:

class CustomHTMLElement {
  constructor (element) {
    this.element = element
  }
  get html () {
    return this.element.innerHTML
  }
  set html (value) {
    this.element.innerHTML = value
  }
}

利用 set/get 实现了对 element.innerHTML 的简单封装。

可是,有时候我们真的需要设置一个私有属性(闭包),然后通过一定的规则来限制对它的修改,利用 set/get就可以轻松实现。

let #age = 1
class Animal {
  constructor(type) {
    this.type = type
  }
  get age() {
    return #age
  }
  set age(val) {
    if (val > 0 && val < 10) {
      #age = val
    }
  }
}

Static Methods

静态方法是面向对象最常用的功能,在 ES5 中利用 function 实现的类是这样实现一个静态方法的。

let Animal = function (type) {
  this.type = type
  this.walk = function () {
    console.log(`I am walking`)
  }
}

Animal.eat = function (food) {
  console.log(`I am eating`);
}

在 ES6 中使用 static 的标记是不是静态方法,代码如下:

class Animal {
  constructor (type) {
    this.type = type
  }
  walk () {
    console.log(`I am walking`)
  }
  static eat () {
    console.log(`I am eating`)
  }
}

有没有很清爽,代码可读性一下子就上来了。

Sub Classes

面向对象只所以可以应对复杂的项目实现,很大程度上要归功于继承。如果对继承概念不熟悉的同学,可以自行查询。在 ES5 中怎么实现继承呢?

// 定义父类
let Animal = function (type) {
  this.type = type
}
// 定义方法
Animal.prototype.walk = function () {
  console.log(`I am walking`)
}
// 定义静态方法
Animal.eat = function (food) {
  console.log(`I am eating`)
}
// 定义子类
let Dog = function () {
  // 初始化父类
  Animal.call(this, 'dog')
  this.run = function () {
    console.log('I can run')
  }
}
// 继承
Dog.prototype = Animal.prototype

从代码上看,是不是很繁琐?而且阅读性也较差。再看看 ES6 是怎么解决这些问题的:

class Animal {
  constructor (type) {
    this.type = type
  }
  walk () {
    console.log(`I am walking`)
  }
  static eat () {
    console.log(`I am eating`)
  }
}

class Dog extends Animal {
  constructor () {
    super('dog')
  }
  run () {
    console.log('I can run')
  }
}

虽然 ES6 在类的定义上仅是 ES5 定义类的语法糖,但是从开发者的角度而言,开发更有效率了,代码可阅读性大大提升。

练习

  1. 请实现一个堆栈类,具备 push、pop 功能。
  2. 请回忆下自己在业务中有哪些场景可以用类来实现。

标签: javascript ES6

评论(0) 浏览(642)

ES6中类的继承

2019-11-5 张宇童 学习日志

相比ES5中类的继承ES6中类的继承语法更加简单明了


class Animal {
    constructor(type) {
        this.type = type
    }
   eat () {
        console.log(`我是small ${this.type},我要吃`)
    }
}

class Dog extends Animal {}

let dog = new Dog('dog')

dog.eat() // 输出 我是small dog,我要吃


在看下面一个example

class Animal {
    constructor(type) {
        this.type = type
    }
    eat () {
        console.log(`我是small ${this.type},我要吃`)
    }
}

class Dog extends Animal {
    constructor(type, age) {
        super(type)
        this.age = age
    }
    say() {
        console.log(`我快${this.age}岁了`)
    }
}
let dog = new Dog('dog', 2)
dog.eat()
dog.age()

标签: javascript ES6

评论(0) 浏览(538)

ES5中类的继承

2019-11-5 张宇童 学习日志

ES5中类的继承实现相对是比较麻烦的

let Animal = function (type) {
    this.type = type
}

Animal.prototype.eat = function () {
    console.log('我要吃')
}

let Dog = function () {
    Animal.call(this, 'dog')
}

Dog.prototype = Animal.prototype

let dog = new Dog()
dog.eat()
这里需要说明的是call(this, args)
call是指将Animal中的this指向Dog类中

标签: javascript ES5 继承

评论(0) 浏览(549)

ES6中的静态方法

2019-11-5 张宇童

相比在ES5中的静态方法ES6定义的更加清晰,在ES6中定义一个静态方法只需要一个static关键字

下面请看example


class Animal {
    constructor(type) {
        this.type = type
    }
    eat () {
        Animal.walk()
        console.log('我在吃')
    }
    static walk() {
        console.log('我要走过去')
    }
}

(new Animal('小狗')).eat()

标签: javascript ES6 静态方法

评论(0) 浏览(659)

ES5中的静态方法

2019-11-5 张宇童 学习日志

静态方法是什么?

静态方法是指一个类的方法。通过类的实例调用这个方法是调用不到的。

只能通过类调用

下面看一下例子

let Animal = function (type) {
    this.type = type
} 
Animal.prototype.eat = function () {
    Animal.walk()
    console.log('我在吃东西')
}
Animal.walk = function () {
    console.log('先走了过去')
}
let dog = Animal('dog')
dog.eat()

代码解读,首先我申明了一个类。
在类的原型上我定义了一个方法
而后在类上我定义了一个walk的方法 注意⚠️定义在类上面的方法只能通过类来调用
最后我在eat方法中调用了这个walk
运行程序执行成功
而这时候我如果通过this来调用walk呢?
那么结果是一定的程序会报错

标签: javascript 类的方法

评论(0) 浏览(520)

关于JavaScript中的getter和setter

2019-11-5 张宇童 学习日志

在没有ES6 之前如果私有化一个类的属性是的确不易的。不能说ES5完全做不到但是确实是十分费力气的一件事

但是现在在ES6中有getter以及setter将使私有化变得十分容易

大家先看一下例子

let _age = 10;
class Animal{
    constructor(type) {
        this.type = type
    }
    get age () {
        return _age;
    }
    set age (value) {
        if (value < 12 && value > 10) {
            _age = value
         }
    }
    age () {
        console.log(`${type}在吃饭`)
    }
}
let dog = new Animal('小狗')
console.log(dog) // 这个时候一定只会输出一个属性那就是type
// 我们来看一下age 
console.log(dog.age) // 这个时候是10
// 我们尝试修改它一下
dog.age = 100
console.log(dog.age )
// 结果我们发现age 并没有改变,这是为什么呢?
通过上面的例子。我们可以看出age的value并没有变为100
因为我们set age的时候加了一个判断条件 这个值必须为11的时候才可以被改变
换句话说这个者其实并不是dog实例中的age。而dog实例中的age 相当于一个入口。而真正意义上的值在_age上面。
还有一个问题为什么我不将age中的值写在this里面呢?
因为语法特性目前还不支持私有。只能以闭包的形式写在上面    

标签: javascript ES6

评论(0) 浏览(479)

Django获取URL

2019-11-5 张宇童 Python

下边代码内容是关于Django获取当前页面URL的代码,应该是对小伙伴有所用。

request.get_full_path()

获取不带参数URL

request.path

获取主机地址

request.get_host()

标签: python 学习笔记 Django

评论(0) 浏览(489)

深入理解Django中间件

2019-11-5 张宇童 Python

一、django生命周期

写中间件之前呢?还是来说说django的生命周期,我再画下图贴上来,比较清楚 1、浏览器向127.0.0.1:8000/index这个url发出一个请求 2、首先网络通信都是基于socket的,那么django一样,通过wsgiref协议来的 3、在socket对来的请求进行一系列处理,将请求带来的数据处理到request对吧 4、然后再走到中间件 5、通过路由层,分发到对应的视图函数, 6、在视图函数中可能会去数据库取数据,到模板层渲染模板好了之后, 7、再通过中间件,再到wsgiref进行处理,返回到浏览器前端

对于模板层,视图层,路由层就十分熟悉不过了,就是多了一个中间件的那一层,
从上面也应该能够知道,前端浏览器来的请求还是我后台django的响应,是不是都要走到中间件那层,
那我们是不是在请求来的时候,先在中间件那里对请求做一些处理,再响应的时候,又做一次处理呢?

20181125185518228044.png

二、中间件

下面就说说怎么自定义一个中间件,那应该怎么去定义呢?
不知道的话,那就是去看看django它的中间件是怎么样的,我们跟着写不就好了?
在settings文件有个MIDDLEWARE的变量,它就是装着中间件的列表
MIDDLEWARE = [ django.middleware.security.SecurityMiddleware, django.contrib.sessions.middleware.SessionMiddleware, django.middleware.common.CommonMiddleware, django.middleware.csrf.CsrfViewMiddleware, django.contrib.auth.middleware.AuthenticationMiddleware, django.contrib.messages.middleware.MessageMiddleware, django.middleware.clickjacking.XFrameOptionsMiddleware,
    ]
里面都是字符串,我应该怎么操作才能看到信息(这里可能有点啰嗦,但我还是要写,你知道可能别人不知道吧)
复制其中的一个元素,将它的格式改为下面这种格式 from django.middleware.clickjacking import XFrameOptionsMiddleware # 没错这就是导包的方法,那请问django它内部是怎么处理这个的呢? # 我猜测,只是猜测,无非就是在最前面加form,最后一个小数点改为import,就和上面不就一样了么。 # 然后你再Ctrl+左击鼠标就能进去看源码了 # 额额,再啰嗦一句,怎么看一个对象的源码,首先你要打印type(对象),在终端复制它的class到文件, # 然后操作和上面就是一样了  你多复制几个django自带的中间件,你就会发现,这其实就是一个类,并且继承了MiddlewareMixin这个类,
该类下面的隐藏方法不用去管,就看其他方法有没有相同之处,(这里你最好是自己去操作一下,这样才有印象)

无非就是下面这几个方法: def process_request(self,request) pass def process_response(self,request,response) pass def process_view(self, request, callback, callback_args, callback_kwargs) pass 看名字就应该能知道是干什么的吧,process_request:不就是处理请求嘛,process_response:处理响应。 # 之前讲到浏览器来的请求,django的响应都会走到中间件,那么上面这么方法是不是也会执行到呢? # 如果可以的话,我就可以在中间件控制请求和响应了

 

三、process_request方法

自定义一个中间件: 1、创建一个py文件,在里面写一个类,继承MiddlewareMixin 2、再到settings里的MIDDLEWARE,添加刚刚创建类 # 注意,1、你别光创建类,然后那么方法就不写了。 2、根据需求写对应的方法,不一定要全部都写。

下面是我写的中间件
zhuyu.py: from django.utils.deprecation import MiddlewareMixin class Test1(MiddlewareMixin): def process_request(self, request): print(test1 request zhuyu) class Test2(MiddlewareMixin): def process_request(self, request): print(test2 request zhuyu)

settings文件中:
MIDDLEWARE = [ django.middleware.security.SecurityMiddleware, django.contrib.sessions.middleware.SessionMiddleware, django.middleware.common.CommonMiddleware, django.middleware.csrf.CsrfViewMiddleware, django.contrib.auth.middleware.AuthenticationMiddleware, django.contrib.messages.middleware.MessageMiddleware, django.middleware.clickjacking.XFrameOptionsMiddleware, zhuyu.Test1, zhuyu.Test2,
    ]

好了,我现在启动项目,并且发一个请求
如果我没有猜错的话,应该会打印我定义的那两句话
结果毋庸置疑:
    test1 request zhuyu
    test2 request zhuyu

我们先看,先打印的是test1,然后才是test2,再去看settings中看看中间件配置
zhuyu.Test1在zhuyu.Test2上方

推断:我们是不是可以判断中间的执行循序是从上到下的呢?

 

四、process_response方法

process_request方法讲了,那在自定义中间中,再添加process_response def process_response(self,request,response): print (test1 response) def process_response(self,request,response): print (test2 response)


这时我们django项目会报错,
错误信息为:NoneType object has no attribute get 先不看这个错误,看看我终端的打印情况

打印情况为: test1 request zhuyu
           test2 request zhuyu
           test2 response
           test1 response
分析:前两行的我们知道原因,那为什么第三,四行却是test2,test1呢?
     难道不是从上往下的顺序?
解答:首先计算机是不会出问题,出现这种情况,我们只能从结果反推测它怎么运行,
     什么时候会走到第三,四行,django处理完数据,响应回前端浏览器,走到中间件,
     先走到zhuyu.Test2,再是zhuyu.Test1,那我们就应该知道些什么了

总结:1、浏览器过来的请求,执行顺序是从上往下,依次执行中间件里的process_request方法 2、后台的响应,执行顺序为从下往上,依次执行process_response方法


再说说那个错误:NoneType object has no attribute get 分析:出现这个错误是我们在给原先的中间件,添加process_response方法,那肯定是方法那里写的有问题
     我们看看那个方法: def process_response(self,request,response): print (test2 response)

    执行该方法的时候,是我们后台已经处理完毕,然后返回到前台浏览器的过程
    那这几个参数代表些什么?
    request:代表request对象,也是前台来的请求对象
    response:这个呢?就是我们视图函数返回的HttpResponse对象,你说是不是,
    这个对象不应该是从中间件走到wsgiref那里进行处理,再到前台吗?
    再看我们的方法,它的返回值是None,所以才会报出上面那个错误

解决方法:让这个方法的返回值是response就好了,return response
思考:这个response就是HttpResponse对象,那我们是不是可以在该方法对这个response进行处理,
     再 return response

跟着上面的总结继续写: 3、如果执行process_response方法,必须返回response这个参数

你是不是有这样的一个疑问?
    为什么我process_request方法,它的返回值是None,为什么不会出错呢?

好吧!这其实是我疑问,为了解决这个疑问,我去看了它的父类MiddlewareMixin
下面是该类的代码: class MiddlewareMixin(object): def __init__(self, get_response=None):
        self.get_response = get_response
        super(MiddlewareMixin, self).__init__() def __call__(self, request):
        response = None if hasattr(self, process_request):
            response = self.process_request(request) if not response:
            response = self.get_response(request) if hasattr(self, process_response):
            response = self.process_response(request, response) return response

看到这段代码我大概知道中间件是怎么去执行的了,(这里我不太确定yy的对不对) 1、首先settings中MIDDLEWARE是一个列表(也就是可迭代的),那应该就是对MIDDLEWARE进行for循环 2、每循环出一个元素,这里是一个字符串,怎么知道到对应的类呢?我猜应该是和上面上个方法一样,对字符串进行修改 3、找到......................................................... 4、这里我卡住了,我知道大概顺序,但是不知道其内部到底怎么运行,难受,其实就是反射,__call__方法,哎,难弄!!! 5、抽空还是用弄清楚。。卡在这里不舒服,知道的话可以邮件我1923790430@qq.com 6、这里1,2的推断失效!

 

五、process_view方法

process_view(self, request, callback, callback_args, callback_kwargs) # 这里的request就是和视图函数的request一样 # callback,该request中的url对应的视图函数 # callback_args, callback_kwargs 该试图函数的有名参数,无名参数 # 你也可以在这里调用,其实没多大的意义 # 继续接着创建的中间件添加该方法,看看执行顺序 process_view(self, request, callback, callback_args, callback_kwargs): print (test1  view)
process_view(self, request, callback, callback_args, callback_kwargs): print (test2  view)

执行看看效果:
        test1 request zhuyu
        test2 request zhuyu
        test1  view
        test2  view
        test2 response
        test1 response
继续测试,我访问127.0.0.1:8000/index,并且在视图函数打印index,再看执行顺序
        test1 request zhuyu
        test2 request zhuyu
        test1  view
        test2  view
        index
        test2 response
        test1 response
基于上面的测试,总结下执行顺序:
process_request----->process_view----->执行视图函数----->process_response

 

六、再看process_request方法

 它的返回值只能是是HttpResponse对象和None,None代表继续走下一个中间件的process_request方法,
    如果是HttpResponse,则会执行当前的process_response方法,相当于跳过了process_view和视图函数那块,
    为什么说只能是HttpResponse对象和None,因为有一层判断,判断process_request的返回值,为真的话,拿着
    该返回值HttpResponse执行process_response方法,因为process_response方法的参数必须为HttpResponse def __call__(self, request):
        response = None if hasattr(self, process_request):
            response = self.process_request(request) if not response:
            response = self.get_response(request) if hasattr(self, process_response):
            response = self.process_response(request, response) return response
    这就是上面的依据,get_response这个方法我猜测一定是一直执行各个中间件的process_request,里面肯定是对象的
    调用,所以才会继续执行这个__call__方法,执行完所有process_request之后,再从最后一个中间件慢慢往上执行
    process_response。

 20181125185518393089.png 20181125185518819863.png

七、process_exception方法

process_exception(self, request, exception)
request这个就不再说了
exception是视图函数异常产生的Exception对象。
这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象
,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个
中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。

 

八、process_template_response

该方法对视图函数返回值有要求,必须是一个含有render方法类的对象,才会执行此方法 class Test: def __init__(self,status,msg):
        self.status=status
        self.msg=msg def render(self): import json
        dic={status:self.status,msg:self.msg} return HttpResponse(json.dumps(dic)) def index(response): return Test(True,测试)

标签: python 学习笔记 Django

评论(0) 浏览(1019)

Django自定义中间件Middleware

2019-11-5 张宇童 Python

from django.utils.deprecation import MiddlewareMixin
# from django.http import HttpResponse
from django.shortcuts import HttpResponse, redirect
 
 
# 方式一:
class MyMiddleware(MiddlewareMixin):
    def process_request(self, request):
        next_url = request.path_info
        if not request.path_info.startswith("/login/"):
            # 做登录验证
            login_flag = request.session.get("login", "")
            if not login_flag:
                return redirect("/login/?next={}".format(next_url))
 
    def process_view(self, request, view_func, view_args, view_kwargs):
        pass
 
    def process_exception(self, request, exception):
        if isinstance(exception, ValueError):
            return HttpResponse("404")
 
    def process_response(self, request, response):
        return response # 这里必须返回response
 
 
# 方式二:
class SimpleMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response
        # 一次性配置和初始化。
 
    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.
        # 这里写的代码会在视图被调用前执行来处理请求
        response = self.get_response(request)
        # 这里写的代码会在视图调用后执行来处理响应
        # Code to be executed for each request/response after
        # the view is called.
 
        return response
 
    def process_view(self, request, view_func, view_args, view_kwargs):
        pass
 
    def process_exception(self, request, exception):
        pass
 
    def process_template_response(self, request, response):
	pass

# 在settings.py里的下面列表中添加自定义的中间件来激活该中间件
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'mymiddlewares.middlewares.MyMiddleware',
    'mymiddlewares.middlewares.SimpleMiddleware',
]

标签: python3 中间件

评论(0) 浏览(556)

Powered by emlog