20230109
说一说this指向(普通函数、箭头函数)?
this的指向
(1)在全局环境中的this————window
在全局环境中,this的指向为window
console.log(this);//window
console.log(this===window);//true
(2)函数中的this————window
在函数内部,this的值取决于函数被调用的方式。全局环境中函数里的this指向window。
function ikun(){
console.log(this);
}
ikun();//window
(3)函数在严格模式下————undefined
function ikun() {
"use strict"
console.log(this); //undefined
}
ikun()//undefined
(4)对象中的this————指向调用者
function fun() {
console.log(this.name);
}
let obj = {
name: '奥特曼',
fn: fun
}
var name = "怪兽"
obj.fn() //奥特曼
fun() //怪兽
obj.fn()是obj调用的所以去找obj里面的name
fun是window调用的所以去找全局里面的this.name
var obj1 = {
name: '怪兽',
f: function () {
console.log('姓名:' + this.name);
}
}
var obj2 = {
name: '奥特曼'
}
obj2.f = obj1.f
obj1.f() //姓名:怪兽
obj2.f() //姓名:奥特曼
把obj1.赋值给obj2.f,则obj2也有了f方法
function foo() {
console.log(this.a);
}
var obj2 = {
a:2,
fn:foo
}
var obj1={
a:1,
o1:obj2
}
obj1.o1.fn() //2
找到的最后是obj2的this对象。
使用new操作符
(1). 在内存中创建一个新对象
(2). 在这个新对象的内部的prototype特性被赋值为构造函数的prototype属性。
(3). 构造函数内部的this被赋值为这个新对象
(4). 执行构造函数内部的代码
(5). 如果执行构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。
new 关键字构建好了一个新对象,并且构造函数中的 this 其实就是新对象本身。
function Person(name){
this.name=name;
this.sayName=function(){
console.log(this.name);
}
}
let person1=new Person("bob");
let person2=new Person("cai");
person1.sayName();//bob
person2.sayName();//cai
箭头函数this指向:箭头函数并不会创建其自身的执行上下文,所以箭头函数中的 this 取决于它的外部函数。
说一说CSS尺寸设置的单位
- px: pixel,像素的缩写,绝对长度单位,它的大小取决于屏幕的分辨率,是开发网页中常常使用的单位。
- em: 相对长度单位,em的单位是相对于父元素计算的,子元素的1em相当于10px,因此10em相当于120px。如下代码:
<head>
<style>
div{
font-size:10px;
}
p{
width:10em;
height:10em;
border:1px solid red;
}
</style>
</head>
<body>
<div>
<p></p>
</div>
结果如图:
如当前父级元素的字体尺寸未设置,由于字体大小可继承的原因,可逐级向上查找,最终找不到则相对于浏览器默认字体大小。
3. rem 相对单位长度,rem的单位是相对于根元素计算的,示例代码如下。
<head>
<style>
html{
font-size:14px;
}
div{
font-size:10px;
}
p{
width:10rem; /*结果为140px */
height:10rem; /*结果为140px */
border:1px solid red;
}
</style>
</head>
<body>
<div>
<p></p>
</div>
媒体查询
媒体查询根据窗口的宽度、屏幕比例和设备方向等差异改变页面的显示方式。在不改变页面内容的条件下,为输出设备提供特定效果。
<style>
@media screen and (min-width:640px){
css属性:css属性
}
</style>
具体实例:
<head>
<style>
/*1.超小设备(小于576px)布局容器宽度为100% */
@media screen and (max-width:575px){
.container{
width:100%;
}
}
/*2.平板设备(大于等于576px)布局容器宽度为540px */
@media screen and (max-width:575px){
.container{
width:540px;
}
}
/*3.桌面显示器(大于等于768px)布局容器宽度为720px */
@media screen and (max-width:768px){
.container{
width:720px;
}
}
/*4.桌面显示器(大于等于992px)布局容器宽度为960px */
@media screen and (max-width:992px){
.container{
width:960px;
}
}
.container{
height:50px;
border:1px solid red;
}
</style>
</head>
<body>
<div class="container"></div>
<script>
</script>
</body>
利用媒体查询和rem单位,实现元素大小的动态变化
<head>
<style>
/*1.超小设备(小于576px)布局容器宽度为100% */
@media screen and (max-width:575px){
.container{
width:100%;
}
}
/*2.平板设备(大于等于576px)布局容器宽度为540px */
@media screen and (max-width:575px){
html{
font-size:20px;
}
}
/*3.桌面显示器(大于等于768px)布局容器宽度为720px */
@media screen and (max-width:768px){
html{
font-size:50px;
}
}
/*4.桌面显示器(大于等于992px)布局容器宽度为960px */
@media screen and (max-width:992px){
html{
font-size:80px;
}
}
div{
width:4rem;
height:4rem;
background-color:pink ;
}
</style>
</head>
<body>
<div>测试文本</div>
<script>
</script>
</body>
20230110
说几个未知宽高元素水平垂直居中方法
transform:translate(x-value,y-value);
平移指元素的位置变化,包括水平和竖直移动。
1.第一种是使用绝对定位的方法
通过left,top以及transform属性使元素水平垂直居中,其中translate属性取值为负数时表示向左和向下移动.
<head>
<style>
div{
position:absolute;
width:100px;
height:100px;
left:50%;
top:50%;
transform: translate(-50%,-50%);
border:1px solid red;
}
</style>
</head>
<body>
<div>测试文本</div>
2.第二种方法是把元素的父盒子设置为弹性盒子
通过设置父盒子为弹性盒子,并设置子元素在主轴上居中对齐(justify-content:center;)再设置子元素在主轴和侧轴上居中对齐(align-items:center)
<head>
<style>
* {
margin: 0;
padding: 0;
}
body{
display:flex;
justify-content: center;
align-items:center;
}
div{
width:100px;
height:100px;
border:1px solid red;
}
</style>
</head>
<body>
<div>
</div>
<script>
</script>
</body>
3.第三种是把父盒子设置为表格元素
通过设置text-align:center;和vertical-align:middle;将子元素水平垂直居中,需要把子元素设置为行内块元素,因为行内元素不能设置宽高
<head>
<style>
* {
margin: 0;
padding: 0;
}
.box{
height:900px;
width:1000px;
display:table-cell;
text-align:center;
vertical-align: middle;
border:1px solid red;
}
div{
width:100px;
height:100px;
border:1px solid red;
display:inline-block;
}
</style>
</head>
<body>
<div class="box">
<div>
</div>
</div>
<script>
</script>
</body>
4.还有一种方法是将父盒子设置为网格元素(dispaly:grid)
设置元素的父级为网格元素display: grid
,设置父级和盒子内部子元素水平垂直都居中justify-content:center; align-items:center
,这种方式代码简介,但是兼容性ie 10以上支持
说一说JS变量提升?
1.变量提升
console.log(a); //undefined
var a=6;
在使用var时,以上代码不会报错,因为使用var声明的变量,在预编译期间会将变量声明与函数声明提升至其对应的作用域最顶端。
以上代码js的执行过程如下:
var a;
console.log(a);//undefined
a=6;
js的var把变量声明,提升到执行上下文最顶端,然后去执行剩下的编译代码,变量提升只提升声明,不提升赋值。
2.js函数提升
先看一个例子:
express(); //ikun
function express(){
console.log("ikun");
}
以上代码正常运行并输出ikun,函数声明会添加到执行上下文顶部,相当于如下代码:
var express=function(){
console.log("ikun");
}
express();
如果把函数声明改为等价的函数表达式,执行的时候就会出错。
express(); //报错
let express=function(){
console.log("ikun");
}
以上代码出错,这里并没有用函数声明的方式,而用的是函数表达式的方式,所以并不存在函数整体提升,仅仅也只是声明提升
总结
变量提升指的是js的变量和函数声明会在代码编译期,提升到代码最前面。
变量提升是指用var关键字进行声明的变量,只有声明被提升,赋值不会提升。函数的提升是声明和赋值都提升了。
变量提升的结果,在初始化变量之前访问该变量,返回的是undefined。在函数声明之前可以调用函数。
说一说 HashRouter 和 HistoryRouter的区别和原理?
1.单页面应用
单页面应用(Single Page Application,SPA),通过URL中的hash(#号)来实现不同页面之间的切换。hash有一个特点,就是HTTP请求中不会包含hash相关内容,单页面程序页面跳转主要通过hash来实现。
在上图中,index.html后面的“#/home”是hash方式的路由,由前端路由来处理。
前端路由在访问一个新页面的时候仅仅是变换了一下hash值而已,没有与服务器端进行交互,所以不存在网络延迟,提升了用户体验。
2.vue-router的工作原理
实现单页面前端路由时,提供了2种方式,分别是hash模式和history模式,根据mode来决定采取哪一种方式。
1.hash模式
vue-router默认为hash模式,使用URL的hash来模拟完整的URL,当URL改变时,页面不会重新加载,#是hash符号,为哈希符或锚点,在hash符号的值为hash值
路由的hash模式是利用了window可以监听onhashchange事件来实现的,hash值是用来指导浏览器动作的,对服务器没有影响,HTTP的请求中也不会包含hash值,每一次改变hash值,都会在浏览器的访问历史中增加一个记录。
2.history模式
hash模式的URL中会自带#,影响URL的美观,而history模式中不会出现#号,这种模式充分利用了history。pushState()来完成URL的跳转。需要在路由配置中增加mode:“history”.
const router=new VueRouter({
mode:history,
routes:[]
});
使用vueRouter代码:
<html>
<head>
</head>
<body>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://unpkg.com/vuex@4.0.0/dist/vuex.global.js"></script>
<script src="https://unpkg.com/vue-router@4.0.15/dist/vue-router.global.js"></script>
<div id="app">
<h1>Hello App!</h1>
<p>
<!--使用 router-link 组件进行导航 -->
<!--通过传递 `to` 来指定链接 -->
<!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
<router-link to="/">Go to Home</router-link>
<router-link to="/about">Go to About</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
<script>
const Home = { template: '<div>Home</div>' }
const About = { template: '<div>About</div>' }
// 2. 定义一些路由
// 每个路由都需要映射到一个组件。
// 我们后面再讨论嵌套路由。
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
]
// 3. 创建路由实例并传递 `routes` 配置
// 你可以在这里输入更多的配置,但我们在这里
// 暂时保持简单
const router = VueRouter.createRouter({
// 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
history: VueRouter.createWebHashHistory(),
routes, // `routes: routes` 的缩写
})
// 5. 创建并挂载根实例
const app = Vue.createApp({})
//确保 _use_ 路由实例使
//整个应用支持路由。
app.use(router)
app.mount('#app')
</script>
</body>
</html>
使用方法:
// hash路由原理
// 监听hashchange方法
window.addEventListener('hashchange',()=>{
div.innerHTML = location.hash.slice(1)
})<br><br>
// history路由原理
// 利用html5的history的pushState方法结合window.popstate事件(监听浏览器前进后退)
function routerChange (pathname){
history.pushState(null,null,pathname)
div.innerHTML = location.pathname
}
window.addEventListener('popstate',()=>{
div.innerHTML = location.pathname
})
总结:
HashRouter和 HistoryRouter的区别:
- history和hash都是利用浏览器的两种特性实现前端路由,history是利用浏览历史记录栈的API实现,hash是监听location对象hash值变化事件来实现
- history的url没有’#'号,hash反之
- 相同的url,history会触发添加到浏览器历史记录栈中,hash不会触发,history需要后端配合,如果后端不配合刷新新页面会出现404,hash不需要
- HashRouter的原理:通过
window.onhashchange
方法获取新URL中hash值,再做进一步处理 - HistoryRouter的原理:通过
history.pushState
使用它做页面跳转不会触发页面刷新,使用window.onpopstate
监听浏览器的前进和后退,再做其他处理 - 需要兼容低版本的浏览器时,建议使用hash模式。 需要添加任意类型数据到记录时,可以使用history模式。
说一说map 和 forEach 的区别?
1.map是什么
map()是数组的一个迭代方法,对数组的每一项都运行传入的函数,返回由每次函数调用的结果构成的数组。
例如,将一个数组的每一项都乘以2,并返回包含所有结果的数组,如下所示:
let numbers=[1,2,3,4,5,6];
let mapResult=numbers.map((item,index,array)=>item*2);//item为当前元素的值 index为当前元素的索引值 array当前元素属于的数组对象
console.log(mapResult);//[2, 4, 6, 8, 10, 12]
forEach():对数组的每一项都运行传入的函数,没有返回值。
let numbers=[1,2,3,4,5,6];
numbers.forEach((item,index,array)=>{item*2;console.log(item);});//item为当前元素的值 index为当前元素的索引值 array当前元素属于的数组对象
console.log(numbers);//1,2,3,4,5,6 forEach不修改原数组
总结
map有返回值,可以开辟新空间,return出一个length和原数组一致的数组,即使数组元素是undefined或null。
forEach默认无返回值,返回结果是undefined,可以通过在函数内部使用索引修改数组。
20230111
说一说事件循环Event loop,宏任务与微任务?
什么是event loop
先看一段代码:
console.log(0);
setTimeout(function(){
console.log(1);
},1000);
console.log(2);
//打印顺序0,2,1
以上代码中,主线程首先会打印0,遇到setTimeout时按照异步处理,1秒后,setTimeout的回调函数会进入任务队列,主线程继续执行打印2,主线程的任务运行完成后,会运行任务队列中的任务,打印1.
因为js是单线程,当一个任务完成后才继续完成下一个任务,当一个任务耗时很长,后一个任务就不得不等着。任务分为2种:
同步任务
在主线程上排队执行的任务,只有前一个任务执行完后,后一个任务才继续执行。
异步任务
不进入主线程,进入“任务队列”的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
执行过程如上图,文字叙述为:
(1) 选择任务队列中的任务,即任务队列中最先进入的任务,如果任务队列为空即null,则执行跳转到微任务(MicroTask)的执行步骤
(2) 将任务队列中的任务设置为已选择任务。
(3) 执行任务
(4)
说一说Vue3.0 实现数据双向绑定的方法 ?
es6的代理和反射提供了拦截并向基本操作嵌入额外行为的能力,给目标对象定义一个关联的代理对象,这个代理对象作为抽象的目标对象来使用。
代理是目标对象的抽象,代理类似于c++指针,可以用作目标对象的替身,又完全独立于目标对象。目标对象既可以直接被操作,也可以通过代理来操作。
创建空代理
空代理:除了作为一个抽象的目标对象,什么也不做。
代理是使用Proxy构造函数创建的,这个构造函数接收2个参数,目标对象和处理程序对象。缺少任何一个对象都会抛出TypeError。
要创建空代理,可以传一个简单的对象字面量作为处理程序对象。可以让所有操作畅通无阻的抵达目标对象。
const target={
id:"target"
};
const handler={};
const proxy=new Proxy(target,handler);//参数1为目标对象,参数2为处理程序对象
//id会访问同一个值
console.log(target.id); //target
console.log(proxy.id); //target
//给目标对象属性赋值会反应到2个对象上
//代理对象Proxy和target对象都指向的是同一个对象
target.id="foo"
console.log(target.id);//foo
console.log(proxy.id);//foo
//给代理对象Proxy属性赋值会反映在2个对象上
//这个赋值会转移到目标对象target上
proxy.id="bar";
console.log(target.id);//bar
console.log(proxy.id);//bar
//hasOwnProperty()方法均可以用在2个对象上
console.log(target.hasOwnProperty("id"));//true
console.log(proxy.hasOwnProperty("id"));//true
//Proxy.prototype是undefined
//因此不能使用instanceof操作符
console.log(target instanceof Proxy);//TypeError
console.log(proxy instanceof Proxy);//typeError
console.log(target===proxy);//false
2.定义捕获器
使用代理的主要目的是可以定义捕获器(trap)。捕获器就是在处理程序对象中定义的“基本操作的拦截器”。每个处理程序对象可以包含0个或多个捕获器,每个捕获器对应一种基本操作,可以直接或间接在处理对象上调用。每次在处理对象上调用这些基本操作时,代理可以在这些操作传播到目标对象之前先调用捕获器函数,从而拦截并修改相应的行为。
当通过代理对象proxy执行get()操作时,会触发定义的get()捕获器。当然,get()不是对象可以调用的方法,这个操作在js代码中通过各种形式触发并被get()捕获器拦截到。proxy[property]、proxy.property或Object.create(proxy)[property]等操作都会触发基本的get()操作以获取属性。
const target={
foo:"bar"
}
const handler={
get(){
return "handler override";
}
}
const proxy=new Proxy(target,handler);
console.log(target.foo);//bar
console.log(proxy.foo);//handler override
console.log(target["foo"]);//bar
console.log(proxy["foo"]);//handler override
console.log(Object.create(target)["foo"]);//bar
console.log(Object.create(proxy)["foo"]);//handler override
捕获器参数和反射API
所有捕获器都可以访问相应的参数,基于这些参数可以重建被捕获方法的原始行为。如get()会接收到目标对象、要查询的属性和代理对象三个参数。
const target={
foo:"bar"
}
const handler={
get(trapTarget,property,receiver){
console.log(trapTarget===target);
console.log(property);
console.log(receiver===proxy);
}
}
const proxy=new Proxy(target,handler);
proxy.foo;
//true
//foo
//true
有了这些参数,就可以重建被捕获方法的原始行为:
const target={
foo:"bar"
}
const handler={
get(trapTarget,property,receiver){
return trapTarget[property];
}
}
const proxy=new Proxy(target,handler);
console.log(proxy.foo);//bar
console.log(target.foo);//bar
不需要手动重建原始行为,可以调用Reflect对象上的同名方法来轻松创建。
三个点的作用
数组的扩展运算符
扩展运算符同样可以运用在对数组的操作中。
可以将数组转换为参数序列
function add(x, y) {
return x + y;
}
const numbers = [4, 38];
add(...numbers) // 42
处理程序对象中所有可以捕获的方法都有对应的反射Reflect方法。这些方法与捕获器拦截的方法具有相同的名称和函数签名,而且也具有与被拦截方法相同的行为。
const target={
foo:"bar"
}
const handler={
get(){
return Reflect.get(...arguments);
}
}
const proxy=new Proxy(target,handler);
console.log(proxy.foo);//bar
console.log(target.foo);//bar
可以更简洁一些:
const target={
foo:"bar"
}
const handler={
get:Reflect.get
}
const proxy=new Proxy(target,handler);
console.log(proxy.foo);//bar
console.log(target.foo);//bar
事实上,如果真的想创建一个可以捕获所有方法,然后将每个方法转发给对应的反射API的空代理,甚至不需要定义处理程序对象。
const target={
foo:"bar"
};
const proxy=new Proxy(target,Reflect);
console.log(proxy.foo);
console.log(target.foo);
总结
Vue3.0 是通过Proxy实现的数据双向绑定,Proxy是ES6中新增的一个特性,实现的过程是在目标对象之前设置了一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
用法:
ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。
var proxy = new Proxy(target, handler);
target: 是用Proxy包装的被代理对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler: 是一个对象,其声明了代理target 的一些操作,其属性是当执行一个操作时定义代理的行为的函数。
在Vue中,Object.defineProperty
无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。
说一说三栏布局的实现方案
1.浮动布局Float
左右模块各自左右浮动,设置中间模块的margin值宽度自适应。这种布局方式,dom结构必须先写浮动部分,再写中间块,否则右浮动会掉到下一行。
<html>
<head>
<style>
.left{
float: left;
width:200px;
border:1px solid red;
}
.right{
float: right;
width:200px;
border:1px solid blue;
}
.center{
margin: 0 ;
border:1px solid green;
}
</style>
</head>
<body>
<div class="left">Left</div>
<div class="right">Right</div>
<!-- 中心布局必须在最后加载 -->
<div class="center">Center</div>
<script>
</script>
</body>
</html>
这种浮动好,兼容性好,但存在局限性,浮动元素脱离文档流,要做清除浮动操作,若处理不好,带来很多问题,如父容器高度塌陷;
2.定位布局Position
让2边的盒子固定到2边留出空位。
<html>
<head>
<style>
.left{
position:absolute;
width:200px;
left:0;
border:1px solid red;
}
.right{
position:absolute;
right:0;
width:200px;
border:1px solid blue;
}
.center{
margin: 0 200px;
border:1px solid green;
}
</style>
</head>
<body>
<!-- 因为不再使用浮动布局,所以可以将中间布局内容提前,以便于优先加载主要内容 -->
<div class="center">Center</div>
<div class="left">Left</div>
<div class="right">Right</div>
<script>
</script>
</body>
</html>
绝对布局的优点就是简单易懂,主要内容可以优先加载,设置方便,不容易出问题。缺点是容器脱离了文档流,后代元素也脱离了文档流,高度未知会有问题。
3.圣杯布局 Holy Grail Layout
利用浮动和负边距来实现。父级元素设置左右的 padding,三列均设置向左浮动,中间一列放在最前面,宽度设置为父级元素的宽度,因此后面两列都被挤到了下一行,通过设置 margin 负值将其移动到上一行,再利用相对定位,定位到两边。
<html>
<head>
<style>
body{
margin: 0 200px;
}
div{
float:left;
}
.left{
margin-left:-100%;
position:relative;
width:200px;
left:-200px;
border:1px solid red;
}
.right{
position:relative;
left:200px;
float:right;
margin-left:-200px;
width:198px;
border:1px solid blue;
}
.center{
width:100%;
border:1px solid green;
}
</style>
</head>
<body>
<!-- 中心布局必须在优先加载 -->
<div class="center">Center</div>
<div class="left">Left</div>
<div class="right">Right</div>
<script>
</script>
</body>
</html>
弹性布局Flex
给父级设置display:flex,通过flex布局来实现三栏布局
<html>
<head>
<style>
body{
display:flex;
}
.left{
width:200px;
border:1px solid red;
}
.right{
width:198px;
border:1px solid blue;
}
.center{
flex:1;
border:1px solid green;
}
</style>
</head>
<body>
<!-- 中心布局必须在优先加载 -->
<div class="left">Left</div>
<div class="center">Center</div>
<div class="right">Right</div>
<script>
</script>
</body>
</html>
总结
三栏布局一般指的是页面中一共有三栏,左右两栏宽度固定,中间自适应的布局,一共有五种实现方式。
这里以左边宽度固定为100px,右边宽度固定为200px为例。
(1)利用绝对定位的方式,左右两栏设置为绝对定位,中间设置对应方向大小的margin的值。
(2)利用flex布局的方式,左右两栏的放大和缩小比例都设置为0,基础大小设置为固定的大小,中间一栏设置为auto。
(3)利用浮动的方式,左右两栏设置固定大小,并设置对应方向的浮动。中间一栏设置左右两个方向的margin值,注意这种方式,中间一栏必须放到最后。
(4)圣杯布局,利用浮动和负边距来实现。父级元素设置左右的padding,三列均设置向左浮动,中间一列放在最前面,宽度设置为父级元素的宽度,因此后面两列都被挤到了下一行,通过设置margin负值将其移动到上一行,再利用相对定位,定位到两边。圣杯布局中间列的宽度不能小于两边任意列的宽度,而双飞翼布局则不存在这个问题。
(5)双飞翼布局,双飞翼布局相对于圣杯布局来说,左右位置的保留是通过中间列的margin值来实现的,而不是通过父元素的padding来实现的。本质上来说,也是通过浮动和外边距负值来实现的。
123
visibility:hidden,display:none,
1.作用不同:
visibility:hidden将元素隐藏,但是在网页中该占的位置还是占着。HTMLyu
display:none将元素的位置显示设为无,在网页中不占任何位置
2、使用后HTML元素有所不同:
visibility:hidden,使用该属性后,HTML元素(对象)仅仅是在视觉上看不见(完全透明),而它所占据的空间位置仍然存在,也即是说它仍具有高度、宽度等属性值。
display:none,使用该属性后,HTML元素(对象)的宽度、高度等各种属性值都将“丢失”。
3、定义不同:
visibility属性指定一个元素是否是可见的。
display这个属性用于定义建立布局时元素生成的显示框类型。
absolute 生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位。元素的位置通过 “left”, “top”, “right” 以及 “bottom” 属性进行规定。
fixed
生成绝对定位的元素,相对于浏览器窗口进行定位。
元素的位置通过 “left”, “top”, “right” 以及 “bottom” 属性进行规定。
relative
生成相对定位的元素,相对于其正常位置进行定位。
因此,“left:20” 会向元素的 LEFT 位置添加 20 像素。
static 默认值。没有定位,元素出现在正常的流中(忽略 top, bottom, left, right 或者 z-index 声明)。
inherit 规定应该从父元素继承 position 属性的值。
100-199 用于指定客户端应相应的某些动作。
200-299 用于表示请求成功。
300-399 用于已经移动的文件并且常被包含在定位头信息中指定新的地址信息。
400-499 用于指出客户端的错误。
500-599 用于支持服务器错误。
100 (Continue/继续)
如果服务器收到头信息中带有100-continue的请求,这是指客户端询问是否可以在后续的请求中发送附件。在这种情况下,服务器用100(SC_CONTINUE)允许客户端继续或用417 (Expectation Failed)告诉客户端不同意接受附件。这个状态码是 HTTP 1.1中新加入的。
101 (Switching Protocols/转换协议)
101 (SC_SWITCHING_PROTOCOLS)状态码是指服务器将按照其上的头信息变为一个不同的协议。这是 HTTP 1.1中新加入的。
200 (OK/正常)
200 (SC_OK)的意思是一切正常。一般用于相应GET和POST请求。这个状态码对servlet是缺省的;如果没有调用setStatus方法的话,就会得到200。
201 (Created/已创建)
201 (SC_CREATED)表示服务器在请求的响应中建立了新文档;应在定位头信息中给出它的URL。
202 (Accepted/接受)
202 (SC_ACCEPTED)告诉客户端请求正在被执行,但还没有处理完。
203 (Non-Authoritative Information/非官方信息)
状态码203 (SC_NON_AUTHORITATIVE_INFORMATION)是表示文档被正常的返回,但是由于正在使用的是文档副本所以某些响应头信息可能不正确。这是 HTTP 1.1中新加入的。
204 (No Content/无内容)
205 (Reset Content/重置内容)
重置内容205 (SC_RESET_CONTENT)的意思是虽然没有新文档但浏览器要重置文档显示。这个状态码用于强迫浏览器清除表单域。这是 HTTP 1.1中新加入的。
206 (Partial Content/局部内容)
206 (SC_PARTIAL_CONTENT)是在服务器完成了一个包含Range头信息的局部请求时被发送的。这是 HTTP 1.1中新加入的。
300 (Multiple Choices/多重选择)
300 (SC_MULTIPLE_CHOICES)表示被请求的文档可以在多个地方找到,并将在返回的文档中列出来。如果服务器有首选设置,首选项将会被列于定位响应头信息中。
301 (Moved Permanently)
301 (SC_MOVED_PERMANENTLY)状态是指所请求的文档在别的地方;文档新的URL会在定位响应头信息中给出。浏览器会自动连接到新的URL。
302 (Found/找到)
与301有些类似,只是定位头信息中所给的URL应被理解为临时交换地址而不是永久的。注意:在 HTTP 1.0中,消息是临时移动(Moved Temporarily)的而不是被找到,因此HttpServletResponse中的常量是SC_MOVED_TEMPORARILY不是我们以为的SC_FOUND。
400 (Bad Request/错误请求)
400 (SC_BAD_REQUEST)指出客户端请求中的语法错误。
401 (Unauthorized/未授权)
401 (SC_UNAUTHORIZED)表示客户端在授权头信息中没有有效的身份信息时访问受到密码保护的页面。这个响应必须包含一个WWW-Authenticate的授权信息头。例如,在本书4.5部分中的“Restricting Access to Web Pages./限制访问Web页。”
403 (Forbidden/禁止)
403 (SC_FORBIDDEN)的意思是除非拥有授权否则服务器拒绝提供所请求的资源。这个状态经常会由于服务器上的损坏文件或目录许可而引起。
404 (Not Found/未找到)
404 (SC_NOT_FOUND)状态每个网络程序员可能都遇到过,他告诉客户端所给的地址无法找到任何资源。它是表示“没有所访问页面”的标准方式。这个状态码是常用的响应并且在HttpServletResponse类中有专门的方法实现它:sendError(“message”)。相对于setStatus使用sendError得好处是:服务器会自动生成一个错误页来显示错误信息。但是,Internet Explorer 5浏览器却默认忽略你发挥的错误页面并显示其自定义的错误提示页面,虽然微软这么做违反了 HTTP 规范。要关闭此功能,在工具菜单里,选择Internet选项,进入高级标签页,并确认“显示友好的 HTTP 错误信息”选项(在我的浏览器中是倒数第8各选项)没有被选。但是很少有用户知道此选项,因此这个特性被IE5隐藏了起来使用户无法看到你所返回给用户的信息。而其他主流浏览器及IE4都完全的显示服务器生成的错误提示页面。可以参考图6-3及6-4中的例子。
核心警告
405 (Method Not Allowed/方法未允许)
405 (SC_METHOD_NOT_ALLOWED)指出请求方法(GET, POST, HEAD, PUT, DELETE, 等)对某些特定的资源不允许使用。该状态码是新加入 HTTP 1.1中的。
406 (Not Acceptable/无法访问)
406 (SC_NOT_ACCEPTABLE)表示请求资源的MIME类型与客户端中Accept头信息中指定的类型不一致。见本书7.2部分中的表7.1(HTTP 1.1 Response Headers and Their Meaning/HTTP 1.1响应头信息以及他们的意义)中对MIME类型的介绍。406是新加入 HTTP 1.1中的。
407 (Proxy Authentication Required/代理服务器认证要求)
407 (SC_PROXY_AUTHENTICATION_REQUIRED)与401状态有些相似,只是这个状态用于代理服务器。该状态指出客户端必须通过代理服务器的认证。代理服务器返回一个Proxy-Authenticate响应头信息给客户端,这会引起客户端使用带有Proxy-Authorization请求的头信息重新连接。该状态码是新加入 HTTP 1.1中的。
408 (Request Timeout/请求超时)
408 (SC_REQUEST_TIMEOUT)是指服务端等待客户端发送请求的时间过长。该状态码是新加入 HTTP 1.1中的。
409 (Conflict/冲突)
该状态通常与PUT请求一同使用,409 (SC_CONFLICT)状态常被用于试图上传版本不正确的文件时。该状态码是新加入 HTTP 1.1中的。
410 (Gone/已经不存在)
410 (SC_GONE)告诉客户端所请求的文档已经不存在并且没有更新的地址。410状态不同于404,410是在指导文档已被移走的情况下使用,而404则用于未知原因的无法访问。该状态码是新加入 HTTP 1.1中的。
411 (Length Required/需要数据长度)
411 (SC_LENGTH_REQUIRED)表示服务器不能处理请求(假设为带有附件的POST请求),除非客户端发送Content-Length头信息指出发送给服务器的数据的大小。该状态是新加入 HTTP 1.1的。
412 (Precondition Failed/先决条件错误)
412 (SC_PRECONDITION_FAILED)状态指出请求头信息中的某些先决条件是错误的。该状态是新加入 HTTP 1.1的。
413 (Request Entity Too Large/请求实体过大)
413 (SC_REQUEST_ENTITY_TOO_LARGE)告诉客户端现在所请求的文档比服务器现在想要处理的要大。如果服务器认为能够过一段时间处理,则会包含一个Retry-After的响应头信息。该状态是新加入 HTTP 1.1的。
414 (Request URI Too Long/请求URI过长)
414 (SC_REQUEST_URI_TOO_LONG)状态用于在URI过长的情况时。这里所指的“URI”是指URL中主机、域名及端口号之后的内容。例如:在URL–http://www.y2k-disaster.com:8080/we/look/silly/now/中URI是指/we/look/silly/now/。该状态是新加入 HTTP 1.1的。
415 (Unsupported Media Type/不支持的媒体格式)
415 (SC_UNSUPPORTED_MEDIA_TYPE)意味着请求所带的附件的格式类型服务器不知道如何处理。该状态是新加入 HTTP 1.1的。
416 (Requested Range Not Satisfiable/请求范围无法满足)
416表示客户端包含了一个服务器无法满足的Range头信息的请求。该状态是新加入 HTTP 1.1的。奇怪的是,在servlet 2.1版本API的HttpServletResponse中并没有相应的常量代表该状态。
注意
在servlet 2.1的规范中,类HttpServletResponse并没有SC_REQUESTED_RANGE_NOT_SATISFIABLE 这样的常量,所以你只能直接使用416。在servlet 2.2版本之后都包含了此常量。
417 (Expectation Failed/期望失败)
如果服务器得到一个带有100-continue值的Expect请求头信息,这是指客户端正在询问是否可以在后面的请求中发送附件。在这种情况下,服务器也会用该状态(417)告诉浏览器服务器不接收该附件或用100 (SC_CONTINUE)状态告诉客户端可以继续发送附件。该状态是新加入 HTTP 1.1的。
500 (Internal Server Error/内部服务器错误)
500 (SC_INTERNAL_SERVER_ERROR) 是常用的“服务器错误”状态。该状态经常由CGI程序引起也可能(但愿不会如此!)由无法正常运行的或返回头信息格式不正确的servlet引起。
501 (Not Implemented/未实现)
501 (SC_NOT_IMPLEMENTED)状态告诉客户端服务器不支持请求中要求的功能。例如,客户端执行了如PUT这样的服务器并不支持的命令。
502 (Bad Gateway/错误的网关)
502 (SC_BAD_GATEWAY)被用于充当代理或网关的服务器;该状态指出接收服务器接收到远端服务器的错误响应。
503 (Service Unavailable/服务无法获得)
状态码503 (SC_SERVICE_UNAVAILABLE)表示服务器由于在维护或已经超载而无法响应。例如,如果某些线程或数据库连接池已经没有空闲则servlet会返回这个头信息。服务器可提供一个Retry-After头信息告诉客户端什么时候可以在试一次。
504 (Gateway Timeout/网关超时)
该状态也用于充当代理或网关的服务器;它指出接收服务器没有从远端服务器得到及时的响应。该状态是新加入 HTTP 1.1的。
505 (HTTP Version Not Supported/不支持的 HTTP 版本)
505 (SC_HTTP_VERSION_NOT_SUPPORTED)状态码是说服务器并不支持在请求中所标明 HTTP 版本。该状态是新加入 HTTP 1.1的。
一、什么是ES6?
ES全称为ECMAScript,它是由国际标准化组织ECMA(全称:European Computer Manufacturers Association)欧洲计算机制造商协会制定的一项脚本语言的标准化规范。JavaScript就实现了这套标准。ES6中的6代表的是版本,从2015年6月份发布的版本及其后续版本统称为ES6,从2015年开始,每年的6月份都会发布新版本。目前各大浏览器产商也都已经支持ES6。
二、 ES6的新特性有哪些?
1.新增了块级作用域(let,const)
2.提供了定义类的语法糖(class)
3.新增了一种基本数据类型(Symbol)
4.新增了变量的解构赋值
5.函数参数允许设置默认值,引入了rest参数,新增了箭头函数。
6.数组新增了一些API,如isArray / from / of 方法;数组实例新增了 entries(),keys() 和 values() 等方法。
7.对象和数组新增了扩展运算符
8.ES6新增了模块化(import / export)
9.ES6新增了Set和Map数据结构。
10.ES6原生提供Proxy构造函数,用来生成Proxy实例
11.ES6新增了生成器(Generator)和遍历器(Iterator)
说说解构赋值
解构:分解数据结构。
赋值:为变量赋值。
解构赋值:从数组或者对象中提取值,按照对应的位置,对变量赋值(在数组解构中,只 要解构的目标可以遍历,就可以实现解构赋值)。
1:数组解构赋值的特点:
在数组解构中,左边用中括号包裹任意个变量,右边则是一个真实的数组,按照 一 一 对应的关系进行赋值,在赋值过程中有以下几种情况:
1.1:声明的变量数量与数组的元素数量一致:
1.2:声明的变量的数量大于数组的元素的数量:
1.3:声明的变量的数量小于数组的元素的数量:
1.4:使用剩余运算符进行解构赋值:
1.5:可遍历对象 例如:字符串:
var、let、const各自的特点及其区别
在ES6之前只有全局作用域和局部作用域,ES6新增了块级作用域let和const,下面将简单介绍var、let和const的特点以及区别。
块级作用域:只能在声明的区域(代码块)中使用,不能在声明的区域的外部使用,否则报(xxx is not defined)。
var:使用var声明的变量,其作用域为该变量所在的函数内,且存在变量提升现象(变量提升:可以先使用再声明),不会受到块级作用域的影响
let:ES6新增的块级作用域
1:使用let声明的变量具有块级作用域:,只能在当前声明的代码块中使用,必须要先声明再使用
2:具有暂时性死区特性。
const:ES6中新增的常量(常量:值(内存地址)不可更改的量)。特点如下:
3.1:具有块级作用域的特点,只能在当前声明的代码块中使用,必须要先声明再使用。
3.2:声明常量时必须赋值,如果不赋值报错:Missing initializer in const declaration(在const声明中未初始化)。
3.3:const声明的常量不可更改。
3.4:使用const声明的常量如果是基本数据类型(比如:数字、字符串),一旦赋值,值不可以更改;如果是引用数据类型(数组,对象),不能重新赋值,但是可以更改数据结构内部的值(比如修改对象中的属性)。
谈谈我对箭头函数的理解
1:箭头函数:ES6新增的定义函数的方式。
2:作用:用来简化函数定义的语法。
3:箭头函数的特点:
3.1:可以将箭头函数赋值给一个变量,变量名字就是函数的名字,通过变量名字调用函数。
3.2:如果函数体中只有一行代码,且代码的执行结果就是返回值,可以省略大括号。
3.3:如果形参只有一个,可以省略小括号(如果没有形参,小括号不能省略)。
Map数据结构
1:Map:类似于对象,是一个存储键值对的集合,但是‘键’’的范围不仅仅是字符串,所有类型的值包括对象都可以当做键。
2:创建Map集合:
2.1:创建Map集合有两种:1.直接new Map()创建实例对象;2.接收一个数组作为参数来创建实例对象(该数组的成员必须是一个个表示键值对的数组)。
3:Map集合常用属性和方法:
3.1:.has(key);查找key,返回布尔值。
3.2:.get(key) 根据key查找value。
3.3:.set(key,value) 添加键值对。
3.4:.size获取存储的数量。
总结
1:var声明的变量是函数变量(全局变量和局部变量),会有变量提升的风险,可以先使用再声明,尽量少用。
2:let声明的变量具有块级作用域的特性,即只能在声明的代码块中使用,只能先声明之后才能使用。
3:const声明的变量是常量,常量的值不允许更改,如果常量是引用类型(对象或者数组),那么可以更改常量内部属性的值。
4:解构就是分解数据结构,赋值就是为变量赋值;在ES6中允许从对象和数组以及任何可以遍历的数据类型进行解构赋值。
5:箭头函数是将函数简化定义的一种方式,将箭头函数赋值给一个变量,变量名就是函数名称,通过调用变量名来调用函数。
6:Set数据结构的声明方式分为两种:1、直接实例化一个空的Set()构造函数;2、在Set构造函数中传入一个数组。Set构造函数可以用来做数组去重等操作。
7:Map数据结构的声明方式分为两种:1、直接实例化一个空的Map()构造函数;2、在Map构造函数中传入一个带有键值对的数组。
1、新增了let const关键字
let var const的区别
let 是代码块有效 var是全局有效
let 是不能重复声明的 var是可以多次声明
let不存在变量的提升 var存在变量的提升
const存储简单数据类型存储的是常量
2、新增的解构赋值
解构赋值针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。
let [a,b]=[1,2]
let {user}={user:“xiaosi”}
3、新增了箭头函数
箭头函数和普通函数的区别
普通函数存在着变量的提升,箭头函数没有
普通函数的this指向,谁调用指向谁,箭头函数是在哪定义就指向谁
普通函数可以当成构造函数,而箭头函数是不可以的
箭头函数没有arguments,要接受所有的参数用…rest
注意
1.父组件push使用this.
r
o
u
t
e
r
.
p
u
s
h
2.
在子组件中获取参数的时候是
t
h
i
s
.
router.push 2.在子组件中获取参数的时候是this.
router.push2.在子组件中获取参数的时候是this.route.params
1 、动态路由传值
1.1 配置动态路由
routes:[
//动态路由参数 以冒号开头
{path:‘/user/:id’,conponent:User}
]
1.2 传值
第一种写法 : 传值
第二种写法 : goToUser(id) {
this.KaTeX parse error: Expected 'EOF', got '}' at position 53: … }̲ 1.3 在对应页面取值…route.params; //结果:{id:123}
2、 Get传值(类似HTMLGet传值)
2.1 配置路由
const routes = [{path:‘/user’,component:User},]
2.2 传值
第一种写法 : 传值
第二种写法 : goToUser(id) {
//‘user’ 是路径名称
this.KaTeX parse error: Expected 'EOF', got '}' at position 61: … }̲ 2.3 在对应页面取值 …route.query; //结果 {id:123}
Tips:路径传递参数会拼接在URL路径后
3 、命名路由push传值
3.1 配置路由
const routes = [{path:‘/user’,name: ‘User’,component:User},]
3.2 传值
goToUser(id) {
//‘User’ 是路径重命名
this.KaTeX parse error: Expected 'EOF', got '}' at position 55: …}); }̲ 3.3 在对应页面取值 …route.params; //结果:{id:123}
Tips:命名路由传递参数不在URL路径拼接显示
1、使用Vue.extend()创建一个组件
2、使用Vue.component()方法注册组件
3、如果子组件需要数据,可以在props中接收定义
4、子组件修改好数据之后,想把数据传递给父组件,可以用emit()方法
data数据结构设计原则:
1、用数据描述所有的内容
2、数据呀结构化,易于程序操作,遍历,查找
3、数据要可扩展,以便增加新的功能
组件设计原则:
1、从功能上拆分层次
2、尽量让组件原子化,一个组件只做一件事情
3、容器组件(只管数据,一般是顶级组件)和展示组件(只管显示视图)
- border
使用 CSS 绘制三角形的第一种方法就是使用 「border」 属性。
给定一个宽度和高度都为 0 的元素,其 border 的任何值都会直接相交,我们可以利用这个交点来创建三角形。也就是说,border属性是三角形组成的,下面给每一边都提供不同的边框颜色:
.triangle {
width: 0;
height: 0;
border: 100px solid;
border-color: orangered skyblue gold yellowgreen;
}
将元素的长宽都设置为0,效果是这样的:
二、内连接
内连接的关键字是 INNER JOIN,INNER JOIN等于CROSS JOIN 等于JOIN。
内连接分为两种情况,一种没有筛选条件;另一种是有ON或者WHERE筛选条件,假设我们有两张这样的表:
create table table_a
(
aid
int(11) NOT NULL AUTO_INCREMENT,
a_name
varchar(255) NOT NULL,
age
smallint NOT NULL,
PRIMARY KEY(aid
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT ‘测试表A’;
create table table_b
(
bid
int(11) NOT NULL AUTO_INCREMENT,
b_name
varchar(255) NOT NULL,
age
smallint NOT NULL,
PRIMARY KEY(bid
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT ‘测试表B’;
插入测试数据
INSERT INTO table_a
(aid, a_name, age) VALUES(1, ‘test1’, 1),(2, ‘test2’, 2),(3, ‘test3’, 3);
INSERT INTO table_b
(bid, b_name, age) VALUES(1, ‘test2’, 2),(2, ‘test3’, 3),(4, ‘test4’, 4);
复制代码
第一种情况:没有筛选条件,此时得到的结果是两张表的笛卡尔积,即3x3=9行
这三条SQL都是等价的
SELECT * FROM table_a JOIN table_b;
SELECT * FROM table_a INNER JOIN table_b;
SELECT * FROM table_a CROSS JOIN table_b;
结果如下:
mysql> SELECT * FROM table_a INNER JOIN table_b;
±----±-------±----±----±-------±----+
| aid | a_name | age | bid | b_name | age |
±----±-------±----±----±-------±----+
| 1 | test1 | 1 | 1 | test2 | 2 |
| 2 | test2 | 2 | 1 | test2 | 2 |
| 3 | test3 | 3 | 1 | test2 | 2 |
| 1 | test1 | 1 | 2 | test3 | 3 |
| 2 | test2 | 2 | 2 | test3 | 3 |
| 3 | test3 | 3 | 2 | test3 | 3 |
| 1 | test1 | 1 | 4 | test4 | 4 |
| 2 | test2 | 2 | 4 | test4 | 4 |
| 3 | test3 | 3 | 4 | test4 | 4 |
±----±-------±----±----±-------±----+
复制代码
第二种情况:有ON和WHERE筛选条件,此时得到的结果是两张表的交集(中间的图),对于内连接,ON和WHERE是等价的,但是对于外连接则不是,在下面会讲到。
这两条SQL是等价的,不过建议使用ON关键字,约定俗成
SELECT * FROM table_a a INNER JOIN table_b b ON a.a_name=b.b_name;
SELECT * FROM table_a a INNER JOIN table_b b WHERE a.a_name=b.b_name;
结果如下:
mysql> SELECT * FROM table_a a INNER JOIN table_b b ON a.a_name=b.b_name;
±----±-------±----±----±-------±----+
| aid | a_name | age | bid | b_name | age |
±----±-------±----±----±-------±----+
| 2 | test2 | 2 | 1 | test2 | 2 |
| 3 | test3 | 3 | 2 | test3 | 3 |
±----±-------±----±----±-------±----+
2 rows in set (0.00 sec)
复制代码
三、自然连接
自然连接的关键字是 NATURAL JOIN,自然连接与内连接的区别在于,自然连接是一种自动寻找连接条件的连接查询,即MySQL会自动寻找相同的字段名作为连接条件,当没有找到连接条件,就变成内连接(没有筛选条件的情况)。
内连接可以通过ON关键字指定不同的字段名
测试表和测试数据依旧如上,由于表A和表B有相同的字段名age,所以MySQL会自动将其作为连接条件:
可以看到第一列是age,与内连接还是有点不太一样的
mysql> SELECT * FROM table_a NATURAL JOIN table_b;
±----±----±-------±----±-------+
| age | aid | a_name | bid | b_name |
±----±----±-------±----±-------+
| 2 | 2 | test2 | 1 | test2 |
| 3 | 3 | test3 | 2 | test3 |
±----±----±-------±----±-------+
复制代码
如果表A和表B没有相同的字段名,则会列出所有结果,这个大家可以自己尝试下。
四、外连接
外连接的关键字是 OUTER JOIN,从上图可以得到(下边的图),MySQL并不支持OUTER JOIN。
之所以有外连接,是因为内连接在一些场景下并不能满足我们的要求,内连接的原理是从每次A表取一条记录去B表中匹配,匹配成功则保留,匹配失败则放弃,直到A表的记录遍历完。
但有时候,我们需要保留匹配失败的记录,比如我们A表是学生表,B表是分数表,当我们拿学生去B表查分数时,如果没找到,我们是比较希望保留该学生的信息的,所以就出现了外连接。
前面提到,MySQL并不支持OUTER JOIN,但是左外连接和右外连接是支持的,他们的区别就在于要保留哪份表的数据。
4.1 左连接
左连接的关键字是 LEFT JOIN,从上图可以得到(左边的图),左连接其实就是两个表的交集+左表剩下的数据 ,当然这是在没其他过滤条件的情况下。
测试表和测试数据依旧和上面一致,测试如下:
没找到的被置为NULL
mysql> SELECT * FROM table_a
a LEFT JOIN table_b
b ON a.a_name=b.b_name;
±----±-------±----±-----±-------±-----+
| aid | a_name | age | bid | b_name | age |
±----±-------±----±-----±-------±-----+
| 2 | test2 | 2 | 1 | test2 | 2 |
| 3 | test3 | 3 | 2 | test3 | 3 |
| 1 | test1 | 1 | NULL | NULL | NULL |
±----±-------±----±-----±-------±-----+
复制代码
4.2 右连接
右连接的关键字是 RIGHT JOIN,从上图可以得到(右边的图),右连接其实就是两个表的交集+右表剩下的数据 ,当然这是在没其他过滤条件的情况下。
mysql> SELECT * FROM table_a
a RIGHT JOIN table_b
b ON a.a_name=b.b_name;
±-----±-------±-----±----±-------±----+
| aid | a_name | age | bid | b_name | age |
±-----±-------±-----±----±-------±----+
| 2 | test2 | 2 | 1 | test2 | 2 |
| 3 | test3 | 3 | 2 | test3 | 3 |
| NULL | NULL | NULL | 4 | test4 | 4 |
±-----±-------±-----±----±-------±----+
复制代码
4.3 ON 与 WHERE
WHERE子句中的过滤条件就是我们常见的那种,不管是内连接还是外连接,只要不符合WHERE子句的过滤条件,都会被过滤掉。
而ON子句中的过滤条件对于内连接和外连接是不同的,对于内连接,ON和WHERE的作用是一致的,因为匹配不到的都会过滤,所以你可以看到内连接并不强制需要 ON 关键字;但是对于外连接,ON决定匹配不到的是否要过滤,所以你可以看到外连接是强制需要 ON 关键字的。
4.4 表的先后顺序
我们知道,连接查询其实就是拿表A的记录去表B中匹配,查询的表称为驱动表,被查询的表称为被驱动表。
前面提到,连接查询是每次从表A拿一条记录去表B匹配,而表B匹配的效率是固定的(不管有没有做索引),所以你可以看到连接查询的效率取决于表A(驱动表)的记录数(行数),这也是为什么人们常说要用“小表驱动大表”的原因。
SQL Server Group By语句
Group By 从字面意义上理解就是根据“By”指定的规则对数据进行分组,所谓的分组就是将一个“数据集” 划分成若干个“小区域”,然后针
对若干个“小区域”进行数据处理。
以下是 GROUP BY 子句的语法:
在此查询语法中, GROUP BY 子句为列中的每个值组合生成一个组。
请考虑以下示例:
在查询中添加一个 GROUP BY 子句来查看效果:
GROUP BY子句和聚合函数
GROUP BY 子句通常与聚合函数一起用于统计数据。
聚合函数对组执行计算并返回每个组的唯一值。 例如, COUNT() 函数返回每个组中的行数。
其他常用的聚合函数是: SUM() , AVG() , MIN() , MAX() 。
GROUP BY 子句将行排列成组,聚合函数返回每个组的统计量(总数量,最小值,最大值,平均值,总和 等)。 例如,以下查询返回客户按年度下达的订单数:
如果要引用 GROUP BY 子句中未列出的任何列或表达式,则必须使用该列作为聚合函数的输入。
否则,数据库系统将会提示错误,因为无法保证列或表达式将为每个组返回单个值。
例如,以下查询将失败:
这是因为 order_status 列未在 GROUP BY 子句中
带有COUNT()函数示例的GROUP BY子句
以下查询返回每个城市的客户数量:
GROUP BY子句带有MIN和MAX函数示例
以下声明返回所有型号年份为 2018 的最低和最高价产品:
带有AVG()函数示例的GROUP BY子句
以下语句使用 AVG() 函数返回型号年份为 2018 年的所有产品的平均价格:
带有SUM函数示例的GROUP BY子句
以下查询使用 SUM() 函数获取每个订单的总价值: