Git Product home page Git Product logo

restart's People

Stargazers

 avatar  avatar

Watchers

 avatar

restart's Issues

Vue组件通信

1.父组件向子组件传值
方法:利用子组件的props。

  1. 子组件用props创建一个或多个属性,用来接收父组件向子组件传递过来的值;
  2. 在父组件中注册子组件;
  3. 在父组件中使用子组件标签,并在标签中添加子组件设置的属性;
  4. 把需要传给子组件的值赋给该属性。

子组件

<p>这是我的名字{{name}}</p>

export default{
  props['name']
}

父组件

<子组件 :name= ' value'> </子组件>

export default{
  data(){
    return{
      value: 'zz'
    }
  }
}

2.子组件向父组件传值

  1. 子组件中需要以某种方式例如点击事件的方法来触发一个自定义事件
  2. 使用$emit(),第一个参数是自定义事件的名字
  3. 将需要传的值作为$emit的第二个参数,该值将作为实参传给响应自定义事件的方法
  4. 在父组件中注册子组件并在子组件标签上绑定对自定义事件的监听

子组件

<button  @click = 'set'  ></button>

methods:{
  set(){
    let a = 10
    this.$emit('setName', a)
  }
}

父组件

<子组件 @set = “hehe = $event”></子组件>

data(){
  return{
    hehe: 'bs'
  }
}

无论是子组件向父组件传值还是父组件向子组件传值,他们都有一个共同点就是有中间介质,子向父的介质是自定义事件,父向子的介质是props中的属性。抓准这两点对于父子通信就好理解了

CORS

CORS

cross-origin resource sharing,跨域资源共享
跨域,指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是给浏览器施加的安全限制。
同源是指域名,协议,端口均相同。

localhost和127.0.0.1虽然都指向主机,但也属于跨域。
一个页面发起的ajax请求,只能是当前页同域名的路径,这能有效的阻止跨站攻击。因此,跨域问题是针对ajax的一种限制。
目前常用的跨域解决方案:

JSONP
最早的解决方案,利用script标签可以跨域的原理实现。
限制:需要服务的支持,只能发起GET请求。

Nginx反向代理
利用Nginx反向代理把跨域为不跨域,支持各种请求方式。缺点:需要额外配置,语义不清晰。

CORS
规范化的跨域请求解决方案,安全可靠。
优势:
在服务端进行控制是否允许跨域,可自定义规则。
支持各种请求方式
缺点:会产生额外的请求。

浏览器会将ajax请求分为两类,简单请求和特殊请求。
简单请求:
同时满足两大条件就属于简单请求
(1)请求方法是以下三种方法之一
HEAD GET POST
(2)HTTP的头信息不超出以下几种字段:
Accept, Accept-Language, Content-Language, Last-Event-ID, Content-Type(只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain)

当浏览器发现ajax请求时简单请求时,会在请求头中携带一个字段:Origin
Origin中会指出当前请求属于哪个域:(协议+域名+端口)服务器会根据这个值决定是否允许其跨域
如果服务器允许跨域,需要在返回的响应头中携带下面信息:
Access-Control-Allow-Origin: http://manage.leyou.com
Access-Control-Allow-Credentials: true

如果跨域请求想要操作cookie,需要满足三个条件:
服务的响应头中需要携带Access-Control-Allow-Credentials并且为true。
浏览器发起ajax需要指定withCredentials 为true
响应头中的Access-Control-Allow-Origin一定不能为*,必须是指定的域名

特殊请求
不符合简单请求的条件,会被浏览器判定为特殊请求,例如请求方式为PUT

预检请求
特殊请求会在正式通信之前,增加一次HTTP查询请求,称为预检请求。浏览器先询问服务器,当前网页域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段,只有得到肯定答复,浏览器才会发出正式的XMLHTTPRequest请求,否则就报错。
OPTIONS /cors HTTP/1.1
Origin: http://manage.leyou.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.leyou.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

Node.js事件循环

事件循环
node.js具有事件驱动和非阻塞I/O的特点。
事件驱动是指nodejs把每一个任务当成事件来处理。非阻塞I/O是指node遇到I/O任务时会从线程池调度单独的线程处理I/O,不会阻塞主线程。
事件循环是尽可能的将相应操作分担给操作系统,从而让单线程的nodejs专注于非阻塞的I/O操作。

反应器模式(Reactor Pattern)
NodeJs 在一个事件驱动的模型中工作,涉及到一个事件多路分用器(Event Demultiplexer) 和一个事件队列(Event Queue)。所有的 I/O 请求最终会生成一个成功或失败的事件或其他的触发器,叫做事件(Event)。根据下面这些算法处理这些事件。

1.事件多路分用器接受 I/O 请求并且委托这些请求给适当的硬件。
2.一旦 I/O 请求处理了(例如:一个文件里里面的数据可以读取,从一个 socket 的数据可以读取等),事件多路分用器为在一个需要处理的队列中的特定行为添加注册的回调处理器。这些回调被称为事件,事件添加的队列被称为事件队列。
3.当在一个事件队列中有可以处理的事件的时候,会按接受它们的顺序循环执行,直到队列为空。
4.如果在事件队列中没有事件或者事件多路分用器没有即将发生的请求,程序会完成。否则,这个过程会从第一步开始继续。

协调整个机制的程序被称为事件循环(Event Loop)。

事件循环是单线程的和半无限循环。当没有更多的工作要做的时候会在某些时候退出。

事件多路分发器
事件多路分发器是反应器模式中一个抽象的概念。在现实世界中,事件多路分发器在不同的系统中实现,名字不同。比如在 Linux 中 epoll,在 BSD(MacOS) 系统中是 kqueue,在 Solaris 中 event ports,在 windows 系统中是 IOCP(Input Output Completion Port)等等。NodeJS 消费被那些实现的低级,非阻塞,异步的硬件 I/O 能力。

文件 I/O 的复杂性
但令人困惑的事实,并不是所有类型的 I/O 可以使用这些实现执行。甚至在一样的操作系统平台上,支持不同类型的 I/O 也有着复杂性。典型的,网络 I/O 使用这些 epoll 和 kqueu,events ports 和 IOCP 可以以非阻塞的方式执行。但是文件 I/O 更加复杂。特定的系统,如 Linux 不支持异步完成文件系统访问。并且在 MacOS 系统上面使用 kquue 的文件系统事件通知和信号有很多局限性。很复杂,几乎不可能解决所有这些文件系统的复杂性以提供完整的异步。

DNS 的复杂性
与文件 I/O 相似,某些被 Node API 提供的 DNS 功能节点也有一定的复杂性。因为 NodeJS DNS 函数如 dns.loogup,访问系统配置文件如 nsswitch.conf,resolve.conf 和 /etc/hosts,上述文件系统的复杂性也适用于 dns.resolve 函数。

Java高级

Java数据结构

Java工具包提供了强大的数据结构,在java中的数据结构主要包括以下几种接口和类:

  • 枚举(Enumeration)
  • 位集合(BitSet)
  • 向量(Vector)
  • 栈(Stack)
  • 字典(Dictionary)
  • 哈希表(Hashtable)
  • 属性(Properties)

枚举

枚举接口虽然本身不属于数据结构,但它在其它数据结构的范畴里应用很广。枚举接口定义了一种从数据结构中取回连续元素的方式。
例如,枚举定义了一个叫nextElement的方法,该方法用来得到一个包含多元素的数据结构的下一个元素。

Enumeration接口
hasMoreElements()测试此枚举是否包含更多的元素。
nextElement()如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。

import java.util.Vector;
import java.util.Enumeration;
 
public class EnumerationTester {
 
   public static void main(String args[]) {
      Enumeration<String> days;
      Vector<String> dayNames = new Vector<String>();
      dayNames.add("Sunday");
      dayNames.add("Monday");
      dayNames.add("Tuesday");
      dayNames.add("Wednesday");
      dayNames.add("Thursday");
      dayNames.add("Friday");
      dayNames.add("Saturday");
      days = dayNames.elements();
      while (days.hasMoreElements()){
         System.out.println(days.nextElement()); 
      }
   }
}

位集合
实现了一组可以单独设置和清除的位或标志。
该类在处理一组布尔值的时候非常有用,你只需要给每个值赋值一位,然后对位进行适当的设置或清除,就可以对布尔值进行操作了。
一个Bitset类创建一种特殊类型的数组来保存位值。Bitset定义了两个构造方法。
第一个构造方法创建一个默认的对象:
BitSet()
第二个方法允许用户指定初始大小。所有位初始化为0.
BitSet(int size)

向量
向量类和传统数组非常相似,但Vector的大小能够根据需要动态的变化。
和数组一样,Vector对象的元素也能通过索引访问。
使用Vector类最主要的好处就是在创建对象的时候不必给对象指定大小,它的大小会根据需要动态的变化。
支持4种构造方法。
第一种构造方法创建一个默认的向量,默认大小为10;
Vector()
第二种构造方法创建指定大小的向量。
Vector(int size)
第三种构造方法创建指定大小的向量,并且增量用incr指定。增量表示向量每次增加的元素数目。
Vector(int size,int incr)
第四种构造方法创建一个包含集合c元素的向量
Vector(Collection c)

除了从父类继承的方法外Vector还定义了以下方法:

序号 方法描述
1 void add(int index, Object element)  在此向量的指定位置插入指定的元素。
2 boolean add(Object o)  将指定元素添加到此向量的末尾。
3 boolean addAll(Collection c) 将指定 Collection 中的所有元素添加到此向量的末尾,按照指定 collection 的迭代器所返回的顺序添加这些元素。
4 boolean addAll(int index, Collection c) 在指定位置将指定 Collection 中的所有元素插入到此向量中。
5 void addElement(Object obj)  将指定的组件添加到此向量的末尾,将其大小增加 1。
6 int capacity() 返回此向量的当前容量。
7 void clear() 从此向量中移除所有元素。
8 Object clone() 返回向量的一个副本。
9 boolean contains(Object elem) 如果此向量包含指定的元素,则返回 true。
10 boolean containsAll(Collection c) 如果此向量包含指定 Collection 中的所有元素,则返回 true。
11 void copyInto(Object[] anArray)  将此向量的组件复制到指定的数组中。
12 Object elementAt(int index) 返回指定索引处的组件。
13 Enumeration elements() 返回此向量的组件的枚举。
14 void ensureCapacity(int minCapacity) 增加此向量的容量(如有必要),以确保其至少能够保存最小容量参数指定的组件数。
15 boolean equals(Object o) 比较指定对象与此向量的相等性。
16 Object firstElement() 返回此向量的第一个组件(位于索引 0) 处的项)。
17 Object get(int index) 返回向量中指定位置的元素。
18 int hashCode() 返回此向量的哈希码值。
19 int indexOf(Object elem)  返回此向量中第一次出现的指定元素的索引,如果此向量不包含该元素,则返回 -1。
20 int indexOf(Object elem, int index)  返回此向量中第一次出现的指定元素的索引,从 index 处正向搜索,如果未找到该元素,则返回 -1。
21 void insertElementAt(Object obj, int index) 将指定对象作为此向量中的组件插入到指定的 index 处。
22 boolean isEmpty() 测试此向量是否不包含组件。
23 Object lastElement() 返回此向量的最后一个组件。
24 int lastIndexOf(Object elem)  返回此向量中最后一次出现的指定元素的索引;如果此向量不包含该元素,则返回 -1。
25 int lastIndexOf(Object elem, int index) 返回此向量中最后一次出现的指定元素的索引,从 index 处逆向搜索,如果未找到该元素,则返回 -1。
26 Object remove(int index)  移除此向量中指定位置的元素。
27 boolean remove(Object o) 移除此向量中指定元素的第一个匹配项,如果向量不包含该元素,则元素保持不变。
28 boolean removeAll(Collection c) 从此向量中移除包含在指定 Collection 中的所有元素。
29 void removeAllElements() 从此向量中移除全部组件,并将其大小设置为零。
30 boolean removeElement(Object obj) 从此向量中移除变量的第一个(索引最小的)匹配项。
31 void removeElementAt(int index) 删除指定索引处的组件。
32 protected void removeRange(int fromIndex, int toIndex)从此 List 中移除其索引位于 fromIndex(包括)与 toIndex(不包括)之间的所有元素。
33 boolean retainAll(Collection c) 在此向量中仅保留包含在指定 Collection 中的元素。
34 Object set(int index, Object element) 用指定的元素替换此向量中指定位置处的元素。
35 void setElementAt(Object obj, int index) 将此向量指定 index 处的组件设置为指定的对象。
36 void setSize(int newSize)  设置此向量的大小。
37 int size()  返回此向量中的组件数。
38 List subList(int fromIndex, int toIndex) 返回此 List 的部分视图,元素范围为从 fromIndex(包括)到 toIndex(不包括)。
39 Object[] toArray() 返回一个数组,包含此向量中以恰当顺序存放的所有元素。
40 Object[] toArray(Object[] a) 返回一个数组,包含此向量中以恰当顺序存放的所有元素;返回数组的运行时类型为指定数组的类型。
41 String toString() 返回此向量的字符串表示形式,其中包含每个元素的 String 表示形式。
42 void trimToSize()   对此向量的容量进行微调,使其等于向量的当前大小。
import java.util.*;

public class VectorDemo {

   public static void main(String args[]) {
      // initial size is 3, increment is 2
      Vector v = new Vector(3, 2);
      System.out.println("Initial size: " + v.size());
      System.out.println("Initial capacity: " +
      v.capacity());
      v.addElement(new Integer(1));
      v.addElement(new Integer(2));
      v.addElement(new Integer(3));
      v.addElement(new Integer(4));
      System.out.println("Capacity after four additions: " +
          v.capacity());

      v.addElement(new Double(5.45));
      System.out.println("Current capacity: " +
      v.capacity());
      v.addElement(new Double(6.08));
      v.addElement(new Integer(7));
      System.out.println("Current capacity: " +
      v.capacity());
      v.addElement(new Float(9.4));
      v.addElement(new Integer(10));
      System.out.println("Current capacity: " +
      v.capacity());
      v.addElement(new Integer(11));
      v.addElement(new Integer(12));
      System.out.println("First element: " +
         (Integer)v.firstElement());
      System.out.println("Last element: " +
         (Integer)v.lastElement());
      if(v.contains(new Integer(3)))
         System.out.println("Vector contains 3.");
      // enumerate the elements in the vector.
      Enumeration vEnum = v.elements();
      System.out.println("\nElements in vector:");
      while(vEnum.hasMoreElements())
         System.out.print(vEnum.nextElement() + " ");
      System.out.println();
   }
}
以上实例编译运行结果如下:

Initial size: 0
Initial capacity: 3
Capacity after four additions: 5
Current capacity: 5
Current capacity: 7
Current capacity: 9
First element: 1
Last element: 12
Vector contains 3.

Elements in vector:
1 2 3 4 5.45 6.08 7 9.4 10 11 12

栈(Stack)
栈实现了一个后进先出(LIFO)的数据结构。
可以把栈理解为对象的垂直分布的栈,当添加一个新元素时,就将新元素放在其他元素的顶部。
当从栈中取元素的时候,就从栈顶取一个元素。换句话说,最后进栈的元素最先被取出。
栈是Vector的一个子类。除了由Vector定义的所有方法,自己也定义了一些方法。

序号 方法描述
1 boolean empty() 测试堆栈是否为空。
2 Object peek( )查看堆栈顶部的对象,但不从堆栈中移除它。
3 Object pop( )移除堆栈顶部的对象,并作为此函数的值返回该对象。
4 Object push(Object element)把项压入堆栈顶部。
5 int search(Object element)返回对象在堆栈中的位置,以 1 为基数。
import java.util.*;
 
public class StackDemo {
 
    static void showpush(Stack<Integer> st, int a) {
        st.push(new Integer(a));
        System.out.println("push(" + a + ")");
        System.out.println("stack: " + st);
    }
 
    static void showpop(Stack<Integer> st) {
        System.out.print("pop -> ");
        Integer a = (Integer) st.pop();
        System.out.println(a);
        System.out.println("stack: " + st);
    }
 
    public static void main(String args[]) {
        Stack<Integer> st = new Stack<Integer>();
        System.out.println("stack: " + st);
        showpush(st, 42);
        showpush(st, 66);
        showpush(st, 99);
        showpop(st);
        showpop(st);
        showpop(st);
        try {
            showpop(st);
        } catch (EmptyStackException e) {
            System.out.println("empty stack");
        }
    }
}
以上实例编译运行结果如下:

stack: [ ]
push(42)
stack: [42]
push(66)
stack: [42, 66]
push(99)
stack: [42, 66, 99]
pop -> 99
stack: [42, 66]
pop -> 66
stack: [42]
pop -> 42
stack: [ ]
pop -> empty stack

字典
字典(Directionary)类是一个抽象类。它定义了键映射到值得数据结构。当想通过特定的键而不是整数索引访问数据时,应该使用Dictionary。由于Dictionary是抽象类,所以它只提供了键映射到值得数据结构,而没有提供特定的实现。

序号 方法描述
1 Enumeration elements( )返回此 dictionary 中值的枚举。
2 Object get(Object key)返回此 dictionary 中该键所映射到的值。
3 boolean isEmpty( )测试此 dictionary 是否不存在从键到值的映射。
4 Enumeration keys( )返回此 dictionary 中的键的枚举。
5 Object put(Object key, Object value)将指定 key 映射到此 dictionary 中指定 value。
6 Object remove(Object key)从此 dictionary 中移除 key (及其相应的 value)。
7 int size( )返回此 dictionary 中条目(不同键)的数量。

这个类已经过时了。在实际开发中可以实现Map接口获取键值的存储功能。

哈希表(Hashtable)
提供了一种在用户定义键结构的基础上来组织数据的手段。
例如,在地址列表的哈希表中,可以根据邮政编码作为键来存储和排序数据,而不是通过人名。

和HashMap很类似,但是它支持同步。
Hashtabel定义了四个构造方法。
第一个是默认构造方法:
Hashtable()
第二个构造函数创建指定大小的哈希表
Hashtable(int size)
第三个构造方法创建了一个指定大小的哈希表,并且通过fillRatio指定填充比例。
填充比例必须介于0.0和1.0直接,它决定了哈希表在重新调整大小之前的充满程度。
Hashtable(int size,float fillRatio)
第四个构造方法创建了一个以M中元素为初始化元素的哈希表。
哈希表的容量被设置为M的两倍。
Hashtable(Map m)

序号 方法描述
1 void clear( ) 将此哈希表清空,使其不包含任何键。
2 Object clone( )创建此哈希表的浅表副本。
3 boolean contains(Object value) 测试此映射表中是否存在与指定值关联的键。
4 boolean containsKey(Object key)测试指定对象是否为此哈希表中的键。
5 boolean containsValue(Object value)如果此 Hashtable 将一个或多个键映射到此值,则返回 true。
6 Enumeration elements( )返回此哈希表中的值的枚举。
7 Object get(Object key) 返回指定键所映射到的值,如果此映射不包含此键的映射,则返回 null. 更确切地讲,如果此映射包含满足 (key.equals(k)) 的从键 k 到值 v 的映射,则此方法返回 v;否则,返回 null。
8 boolean isEmpty( )测试此哈希表是否没有键映射到值。
9 Enumeration keys( ) 返回此哈希表中的键的枚举。
10 Object put(Object key, Object value)将指定 key 映射到此哈希表中的指定 value。
11 void rehash( )增加此哈希表的容量并在内部对其进行重组,以便更有效地容纳和访问其元素。
12 Object remove(Object key)从哈希表中移除该键及其相应的值。
13 int size( ) 返回此哈希表中的键的数量。
14 String toString( )返回此 Hashtable 对象的字符串表示形式,其形式为 ASCII 字符 ", " (逗号加空格)分隔开的、括在括号中的一组条目。
import java.util.*;

public class HashTableDemo {

   public static void main(String args[]) {
      // Create a hash map
      Hashtable balance = new Hashtable();
      Enumeration names;
      String str;
      double bal;

      balance.put("Zara", new Double(3434.34));
      balance.put("Mahnaz", new Double(123.22));
      balance.put("Ayan", new Double(1378.00));
      balance.put("Daisy", new Double(99.22));
      balance.put("Qadir", new Double(-19.08));

      // Show all balances in hash table.
      names = balance.keys();
      while(names.hasMoreElements()) {
         str = (String) names.nextElement();
         System.out.println(str + ": " +
         balance.get(str));
      }
      System.out.println();
      // Deposit 1,000 into Zara's account
      bal = ((Double)balance.get("Zara")).doubleValue();
      balance.put("Zara", new Double(bal+1000));
      System.out.println("Zara's new balance: " +
      balance.get("Zara"));
   }
}
以上实例编译运行结果如下:

Qadir: -19.08
Zara: 3434.34
Mahnaz: 123.22
Daisy: 99.22
Ayan: 1378.0

Zara's new balance: 4434.34

属性(Properties)
properties继承与Hashtable表示一个持久的属性集。属性列表中每个键及其对应值都是一个字符串。
Properties类被许多Java类使用。例如,在获取环境变量时它就作为System.getProperties()方法的返回值。
Properties定义如下实例变量。这个变量持有一个Properties对象相关的默认属性列表。

Properties defaults;

第一个构造方法没有默认值
Properties()
第二个构造方法使用propDefault作为默认值 。两种情况下,属性列表都为空。
Properties(Properties propDefault)

序号 方法描述
1 String getProperty(String key) 用指定的键在此属性列表中搜索属性。
2 String getProperty(String key, String defaultProperty)用指定的键在属性列表中搜索属性。
3 void list(PrintStream streamOut) 将属性列表输出到指定的输出流。
4 void list(PrintWriter streamOut)将属性列表输出到指定的输出流。
5 void load(InputStream streamIn) throws IOException 从输入流中读取属性列表(键和元素对)。
6 Enumeration propertyNames( )按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
7 Object setProperty(String key, String value) 调用 Hashtable 的方法 put。
8 void store(OutputStream streamOut, String description) 以适合使用  load(InputStream)方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。

2.作用域链,this

对于每个执行上下文,都有三个重要属性:

  • 变量对象
  • 作用域链
  • this

作用域链
当查找变量时,会先从当前上下文的变量对象中查找,如果没找到,就从父级执行上下文的变量对象中查找,一直找到全局对象。这个由多个执行上下文的变量对象构成的链表就叫作用域链。

this
当函数直接纯粹的调用时this指向全局对象
作为对象方法调用时this指向调用方法的对象
作为构造函数调用时this指向构造函数创建的对象实例
apply、call、bind调用:this指向这些函数的第一个参数
箭头函数里的this指向父级作用域里的this

HTTP压缩

HTTP压缩是指: Web服务器和浏览器之间压缩传输的”文本内容“的方法。 HTTP采用通用的压缩算法,比如gzip来压缩HTML,Javascript, CSS文件。 能大大减少网络传输的数据量,提高了用户显示网页的速度。当然,同时会增加一点点服务器的开销。HTTP压缩,在HTTP协议中,其实是内容编码的一种。

在http协议中,可以对内容(也就是body部分)进行编码, 可以采用gzip这样的编码。 从而达到压缩的目的。 也可以使用其他的编码把内容搅乱或加密,以此来防止未授权的第三方看到文档的内容。

过程

  1. 浏览器发送Http request 给Web服务器, request 中有Accept-Encoding: gzip, deflate。 (告诉服务器, 浏览器支持gzip压缩)

  2. Web服务器接到request后, 生成原始的Response, 其中有原始的Content-Type和Content-Length。

  3. Web服务器通过Gzip,来对Response进行编码, 编码后header中有Content-Type和Content-Length(压缩后的大小), 并且增加了Content-Encoding:gzip. 然后把Response发送给浏览器。

  4. 浏览器接到Response后,根据Content-Encoding:gzip来对Response 进行解码。 获取到原始response后, 然后显示出网页。

内容编码类型:
HTTP定义了一些标准的内容编码类型,并允许用扩展的形式添加更多的编码。

Content-Encoding header 就用这些标准化的代号来说明编码时使用的算法
Content-Encoding值
gzip  表明实体采用GNU zip编码
compress 表明实体采用Unix的文件压缩程序
deflate  表明实体是用zlib的格式压缩的
identity  表明没有对实体进行编码。当没有Content-Encoding header时, 就默认为这种情况
gzip, compress, 以及deflate编码都是无损压缩算法,用于减少传输报文的大小,不会导致信息损失。 其中gzip通常效率最高, 使用最为广泛。

压缩的好处:对纯文本内容可以压缩为原大小的40%
Gzip的缺点:对JPEG压缩的不够好
原理:简单来说是替换文本中类似的字符串。因为html和css中通常有大量重复标签或字符串等。

Base64

计算机中任何数据都是按ASCII码存储的,而ASCII码的128-255之间的值是不可见字符。而在网络上交换数据时,比如说从A地传到B地,往往要经过多个路由设备,由于不同的设备对字符的处理方式有一些不同,这样,哪些不可见字符就有可能被处理错误,这是不利于传输的。所以就先把数据先做成一个Base64编码,统统变成可见字符,这样出错可能性就大大降低了。

对证书来说,特别是根证书,一般都是作Base64编码的,因为它要在网上被许多人下载。电子邮件附件一般也做Base64编码,因为一个附件数据往往有不可见字符。

  1. 标准base64只有64个字符(英文大小写,数字和+,/)以及用作后缀等号
  2. base64是把3个字节变成4个可打印字符,所以base64编码后的字符串一定能被4整除(不算用作后缀的等号)
  3. 等号一定用作后缀,且数目一定是0个,1个或者2个,这是因为如果原文长度不能被3整除,base64要在后面添加\0凑齐3n位,为了正确还原,添加几个\0就加上几个等号,显然添加等号的数目只能是0或1或2
  4. 严格来说base64不能算是一种加密,只能说是编码转换,使用base64初衷是为了方便把含有不可见字符串的信息用可见字符串表示出来, 以便复制粘贴

前端利用xlsx.js导出EXCEL表格

使用xlsx导出表格
安装依赖:
npm install --save xlsx file-saver

然后在组件/模块中引入

import XLSX from 'xlsx'
import XLSXStyle from 'yxg-xlsx-style'
import { saveAs } from 'file-saver'

使用场景通常是前端表格数据导出为Excel表。表格数据基本上都是从后端返回的对象数组。
xlsx可以将二维数组导出为Excel表格,使用的是utils.aoa_to_sheet。使用前需要将原始数据处理成二维数组。
处理后的二维数组形式示例:

 var data = [
        [ "id",    "name", "value" ],
        [    1, "sheetjs",    7262 ],
        [    2, "js-xlsx",    6969 ]
 ]

const ws = XLSX.utils.aoa_to_sheet(data)
const wb = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(wb, ws, 'sheet1')
XLSX.writeFile(wb, 'SheetJS.xlsx')

也可以将JSON数据导出为Excel表。使用了utils.json_to_sheet,而其它部分和用二维数组导出一样。
无论是用utils.aoa_to_sheet还是utils.json_to_sheet,返回的都是worksheet。创建一个workbook,再把worksheet添加到workbook中,就可以进行导出操作了。

Vue项目代码启动流程

一般一个Vue项目创建后都会有这三个文件:
index.html,main.js,App.vue

index.html: Vue是单页面形式开发,所有组件(扩展名为Vue的所有文件都被视为组件)
都会通过这个文件进行渲染加载。一般不会在这里有过多修改。

main.js
相当于入口文件。里面包含了各种依赖的引入和使用等。其中import A from 'B' 代表引入,Vue.use(A)则表示全局定义了A,定义之后可以在这个Vue项目中的任何地方使用它。
在main.js里会创建一个根组件

App.vue
这是根组件,实际上没什么作用,main.js里创建的根组件就是这个文件。router-view就是要渲染的页面的存放位置,通过路由来改变要渲染的页面。

Cesium 影像服务

Cesium主要提供的影像

ArcGisMapServerImageryProvider
支持ArcGIS Online和Server的相关服务

BingMapsImageryProvider
Bing地图影像,可以指定mapStyle,详见BingMapsStyle类

createOpenStreetMapImageryProvider
OSM影像服务,根据不同的url选择不同的风格

createTileMapServiceImageryProvider
看文档是根据MapTiler规范,貌似是可以自己下载瓦片,发布服务,类似ArcGIS影像服务的过程

GoogleEarthImageryProvider
企业级服务,没有用过

ImageryProvider
基类,所有的影像服务最终都基于此类,如果你需要扩展新的Provider也会继承该类

MapboxImageryProvider
Mapbox影像服务,根据mapId指定地图风格

SingleTileImageryProvider
单张图片的影像服务,适合离线数据或对影像数据要求并不高的场景下

UrlTemplateImageryProvider
指定url的format模版,方便用户实现自己的Provider,比如国内的高德,腾讯等影像服务,url都是一个固定的规范,都可以通过该Provider轻松实现。而OSM也是通过该类实现的。

WebMapServiceImageryProvider
符合WMS规范的影像服务都可以通过该类封装,指定具体参数实现

WebMapTileServiceImageryProvider
服务WMTS1.0.0规范的影像服务,都可以通过该类实现,比如国内的天地图

TileCoordinatesImageryProvider
渲染每一个瓦片的围,方便调试

GridImageryProvider
渲染每一个瓦片内部的格网,了解每个瓦片的精细度

7.ES6-Generator

普通函数:一路到底
生成器函数:可以ti

生成器函数

  1. 在 function 关键字和函数名之间多了一个星号
  2. 函数内部使用了 yield 表达式,用于定义 Generator 函数中的每个状态
  3. Generator 函数通过多个 yield 表达式定义内部状态,掉用 Generator 函数时,不会像普通函数会立即执行,而是返回的是一个 Iterator 对象,通过调用 next() 方法,可以依次遍历 Generator 函数的每个内部状态
    4.Generator 函数的星号的位置有四种情况,一般用第二种。
  • function *gen () {}
  • function* gen () {}
  • function * gen () {}
  • function*gen () {}

yield 关键字在 Generator 函数中有两个作用:定义内部状态和暂停执行。暂时放弃

执行Generator函数不会直接执行,而是返回一个generator对象。在生成器的函数原型上有next方法。

”踹一脚走一步“。当你在一个函数里需要进行异步操作时可以使用Generator来实现。本质上时用一个Generator生成了几个小函数,这也是它叫生成器的原因。

yield既可以传参又可以返回

用返回的generator对象执行next方法时可以传参。传递的参数可以在generator函数里的yield接收。

第一个next执行generator里从函数体开始到第一个yield间的代码。第一个next接受参数没有任何意义,因为第一个yield接受的不是它。想要在第一个yield之前的代码里使用参数就要用generator函数本身传参

yield返回:第一个 yield返回的值是第一个next的返回值

webpack配置

webpack有五个重要概念:entry,output,loader,plugins,mode.分别表示打包入口,打包出口,加载器(用于转换,可以理解为翻译),插件以及打包模式(分为生产环境和开发环境)
在webpack.config.js文件中,需要进行各种配置。

const {resolve} = require('path'); //resolve是path的一个方法,用于拼接路径。path是nodejs的一个用于路径的对象。

const HtmlWebpackPlugin = require('html-webpack-plugin'); //插件。这是个构造函数(类),所以在使用的时候要new出来。

module.exports = {
entry:’helloworld/src/index.js‘,

output:{
filename: 'built.js',
path: resolve(__dirname,"build")
},
module: { //关于loader的配置:
rules:[{
test: /.css$/, //表示以css为后缀的文件
use: ['style-loader','css-loader'] },
{test:/.less$/,
use:['style-loader','css-loader','less-loader' ] //在use里,loader加载顺序由后向前
},
{
test: /.(jpg|png|jpeg|gif)$/,
loader: url-loader, //一个loader用loader,需要多个loader就用use和数组。
//问题:;默认处理不到html文件里的图片。另外url-loader是用es6模块化方式,而html-loader是
//用commonjs,因此解析时会出错。解决:关闭url-loader的es6模块化,使用commonjs.

options: { limit: 8*1024, esModule: false, name: '[hash:10].[ext]' } //图片小于8kb就会被转换成base64。优点:减少请求次数减少服务器压力,缺点:文件变大访问变慢。通常设置8-12kb以下的图片.
// [hash:10]以hash值的前10位命名,[ext]指扩展名不变
},
{
test: /.html$/,
loader: 'html-loader' //专门负责引入html文件里的图片,从而能被url-loader处理。
}
]
},
plugins:{
//html-webpack-plugin插件.功能:默认创建一个空的HTML,自动引入打包输出的所有资源(js/css)。
//需求:需要由结构的HTML文件,
new HtmlWebpackPlugin({
template:’./src/index.html‘ //相当于复制了index.html并把资源引入到这个复制的html文件里。自动引入。
})

}

}

6.ES6-Promise

Promise是一个对象,它是一个构造函数。Promise是异步编程的一种解决方案。

简单来说,Promise是一个容器,里面保存着某个未来才会结束的事情(通常是一个异步操作)。

特点:
对象的状态不受外部影响。Promise对象代表一个异步操作,有三种状态:pending进行中,fulfilled已成功和rejected已失败。

一旦状态改变,就不会再变。Promise改变状态只有两种可能:成功或者失败。

Promise一旦新建就会立即执行,无法中途取消。其次,如果不设置回调函数,内部错误无法反映到外部。

基本用法:Promise是一个构造函数,用来生成Promise实例。

const promise = new Promise(function(resolve,reject){
  if(success){
    resolve(value)
  }else{
    reject(error)
  }
})
promise.then(function(value){
  //成功状态下的操作
},function(error){
  //失败后的操作
})

(1) Promise.all(iterable)
参数
iterable 必须是一个可迭代对象,如 Array 或 String。
返回值
一个新的Promise实例。
Promise.all 的使用
如果传入的参数中存在不是Promise实例,则会先调用Promise.resolve,将其转为Promise实例,再进一步处理。
Promise.all 的异步和同步
如果传入的参数为空的可迭代对象,则同步返回一个已完成(already resolved)状态的 Promise;
非空时则返回一个异步完成(asynchronously resolved) Promise

Promise.all 的快速返回失败行为
传入的参数中任意一个promise返回失败时,那么整体立即返回失败,返回的错误信息是第一个失败的promise结果

(2) Promise. race(iterable)
参数
iterable 必须是一个可迭代对象,如 Array 或 String。
返回值
一个新的Promise实例

Promise.race 的使用
race有赛跑之译,因此返回的新实例状态,是跟随参数中最先改变状态的那个实例;如果不是Promise实例,依旧先用Promise.resolve方法,转化后再进一步处理。
如果传的迭代为空,则返回的 Promise 永远等待

(3) Promise.resolve(value)
返回一个以给定的值解析后的Promise对象;
参数 value 主要有以下几种情况:

一个Promise实例
原封不动的返回该实例;

一个thenable对象:是指含有then方法的对象
跟随这个thenable对象的,采用它的最终状态;

普通数据:[String|Array|Object|Number]
直接将传入参数当最终结果并返回一个新的Promise

无参数
直接返回一个resolved状态的Promise对象

(4) Promise.reject(reason)
参数:表示被拒绝的原因;
⚠️传入的参数会原封不动的作为 reject 函数的理由,并不会因为传入的参数 Promise 或者是 thenable 对象而有所不同

返回值:一个含有reason的状态为rejected的Promise

webpack详细配置

entry入口配置:
可以是:

  1. 字符串: 单入口,打包形成一个chunk,输出一个bundle
  2. 数组: 多入口。所有入口文件最终形成一个chunk,输出一个bundle.一般只有在HMR中让html热更新生效
  3. 对象: 多入口。有几个入口文件就有几个chunk,几个bundle。此时chunk名称就是key。特殊用法: 所有入口只生成一个chunk,一个bundle
    {
    index: [ ],
    add: ” “
    }

output 出口配置
文件名称和输出目录
filename和path
其中path需要使用resolve方法,resolve从path里引入:const resolve = require(’path‘)
publicPath: ’/‘ 所有资源的引入的公共路径的前缀
chunkFilename: '' 非入口chunk的名称

module loader的配置
在rules里写loader配置
modules: {
rules: [
{
test: /.js$/,
use: [] 多个loader用use,单个用loader,
exclude: //排除的文件
include: reslove(__dirname,'src') 只检查src目录下的文件,
enforce: ’pre‘ 优先执行。延后值改为post,
oneOf: [] 以下配置只会生效一个
},{},{}

]
}

resolve(非path里的方法)
解析模块规则

resolve: {
//配置解析模块路径别名
alias: {
$css: resolve(__dirname, 'src/css') : 用$css表示src/css这个路径。缺点是没有提示了
},
extensions: [ 'js' , 'json' ] 配置省略文件路径的后缀名
modules: [ resolve(__dirname, '../../node_modules') , 'node_modules' ] 告诉webpack在解析模块时从哪找目录

}

DNS

Domain Name System,域名系统。实质上是一个域名和IP地址相互映射的联机分布式数据库系统。
作用: 根据域名查出IP地址。
域名比起IP地址更便于记忆,但机器使用的还是IP地址,因为IP地址长度固定便于处理。IPv4为32位,IPv6为128位。
计算机在网络通讯时只能识别IP地址,因为网络通讯大部分是基于TCP/IP协议的,协议基于IP地址。DNS就是把域名翻译成IP地址的东西。

解析要点:
(1)当某一个应用进程需要把域名解析为IP地址时,该应用进程就调用解析程序(resolver),并成为DNS的一个客户,把待解析的域名放在DNS请求报文中,以UDP用户数据报方式(减少开销)发给本地域名服务器。
(2)本地服务器在查找到域名后,把对应的IP地址放在回答报文中返回。应用进程获得目的主机的IP地址后即可进行通信。
(3)若本地域名服务器不能回答该请求,则此域名服务器就暂时成为DNS中的另一名客户,并向其他域名服务器发出查询请求。这种过程直至找到能够回答该请求的域名服务器为止。

DNS解析过程

Cesium即时面积计算(未经验证)


<script>
    export default {
        name: "test",
        data() {
            return {
                chooseButtonOpen: false,
                polygonArray: [],
                point: {},
                polygon: null,
                worldPositionArray: null,
                area: '',
            }
        },
        methods: {
            //获取屏幕坐标点为弧度
            getPositionCartographic(movement) {
                let windowPosition = movement.position || movement.endPosition; //判断是点击还是鼠标移动
                let ray = this.viewer.camera.getPickRay(windowPosition);//此为Cartesian3坐标
                let position = this.viewer.scene.globe.pick(ray, this.viewer.scene); 
                let cartographic = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(position); //转换成弧度
                let latitude = Cesium.Math.toDegrees(cartographic.latitude);//纬度
                let longitude = Cesium.Math.toDegrees(cartographic.longitude);//经度
                let height = cartographic.height;
                let nowPosition = {latitude: latitude, longitude: longitude, height: height};
                return nowPosition;
            },
            //绘制多边形
            drawPolygon() {
                //选中
                this.viewer.entities.removeAll();
                var _this = this;
                var clickPosition;
                var screenSpaceEventHandler = _this.viewer.screenSpaceEventHandler;
                if (this.chooseButtonOpen === false) {
                    this.chooseButtonOpen = true;
                    //先创建一个polygon然后每次点击修改里面的值
                    this.viewer.scene.screenSpaceCameraController.enableRotate = false;
                    screenSpaceEventHandler.setInputAction(function onMouseClick(movement) {
                        clickPosition = _this.getPositionCartographic(movement);
                        _this.point = _this.viewer.entities.add({
                            position: Cesium.Cartesian3.fromDegrees(clickPosition.longitude, clickPosition.latitude, clickPosition.height),
                            point: {
                                pixelSize: 5,
                                color: Cesium.Color.RED,
                                outlineColor: Cesium.Color.WHITE,
                                outlineWidth: 2
                            },
                        });
                        var polygonPositions = Cesium.Cartesian3.fromDegreesArray(_this.polygonArray);
                        if (!_this.polygon) {  //创建一个多边形,只创建一次
                            _this.polygon = new Cesium.Entity({
                                name: "polygon",
                                id: 'polygon',
                                polygon: {
                                    hierarchy: polygonPositions,
                                    material: Cesium.Color.RED.withAlpha(0.3),
                                }
                            });
                            _this.viewer.entities.add(_this.polygon);
                            _this.polygonArray.push(clickPosition.longitude, clickPosition.latitude);
                        } else {
                            _this.polygonArray.push(clickPosition.longitude, clickPosition.latitude);
                            //添加新的坐标点,用CallbackProperty方法,注意需要返回的值,这里是要Cartesian3
                            var _update = function () {
                                return polygonPositions;
                            };
                            _this.polygon.polygon.hierarchy = new Cesium.CallbackProperty(_update, false);
                            _this.polygonArray.push(clickPosition.longitude, clickPosition.latitude);//添加两个数,为后面移动做准备
                        }
                    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
                    //移动鼠标
                    screenSpaceEventHandler.setInputAction(function onMouseMove(movement) {
                        clickPosition = _this.getPositionCartographic(movement);
                        _this.polygonArray.splice(-2, 2, clickPosition.longitude, clickPosition.latitude);
                        var polygonPositions1 = Cesium.Cartesian3.fromDegreesArray(_this.polygonArray);
                        if (_this.polygon) {
                            var _update = function () {
                                return polygonPositions1;
                            };
                            _this.polygon.polygon.hierarchy = new Cesium.CallbackProperty(_update, false);
                            _this.area = _this.polgyonArea(polygonPositions1);
                            _this.area = _this.area.toFixed(2);
                        }
                    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
                    //右键以结束绘制多边形
                    screenSpaceEventHandler.setInputAction(function rightMouseClick(movement) {
                        screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
                        screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
                        _this.polygonArray.splice(-4, 4);
                        var polygonPositions2 = Cesium.Cartesian3.fromDegreesArray(_this.polygonArray);
                        console.log("polygonPositions2", polygonPositions2);
                        if (_this.polygon) {
                            var _update = function () {
                                return polygonPositions2;
                            };
                            _this.polygon.polygon.hierarchy = new Cesium.CallbackProperty(_update, false);
                            _this.area = _this.polgyonArea(polygonPositions2);
                            _this.area = _this.area.toFixed(2);
                        }
                    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
                }
                //未选中
                else {
                    this.chooseButtonOpen = false;
                    this.viewer.scene.screenSpaceCameraController.enableRotate = true;
                    this.point = null;
                    this.polygon = null;
                    this.polygonArray = [];
                    this.area = '';
                }
            },
            //计算多边形面积
            polgyonArea(points) {
                var area = 0;
                for (let i = 0; i < points.length; i++) {
                    let j = (i + 1) % points.length;
                    area += points[i].x * points[j].y;
                    area -= points[i].y * points[j].x;
                }
                area /= 2;
                return Math.abs(area);
            },
        }
    }
</script>

3.原型和原型链

每个函数都有一个prototype属性,只有函数有这个属性。它指向一个名为原型对象的对象。当函数为构造函数时它的原型对象才有意义。
同时,每个对象(除了null)都有一个属性__proto__,它指向这个对象构造函数的prototype。即构造函数的prototype和它创建的实例的__proto__都指向原型对象。因此大多数情况下__proto__都可以理解为构造器的原型,除了Object.create方法创建的对象的__proto__。同时,原型对象也有个属性constructor属性,这个属性指向构造函数。

由于任何对象(除了null)都有__proto__属性,因此函数也有__proto__属性。__proto__的指向取决于对象创建时的方式。普通实例对象由构造函数创建,此时实例的__proto__指向实例对象的构造函数的prototype。

每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链的基本概念。

所有对象都继承于Object,原型链的顶端就是Object.prototype。即Object.prototype.proto
= null
所有函数的__proto__都一样,因为所有函数在作为实例对象时都是被Function构造的,所以都指向Function的原型对象。Function由自己构造,因此Function的隐式原型__proto__(作为对象时)和显示原型prototype(作为构造函数时)都指向同一个原型对象。

①__proto__和constructor属性是对象所独有的;
② prototype属性是函数所独有的,因为函数也是一种对象,所以函数也拥有__proto__和constructor属性。

for循环中i++和++i的区别

前置自增++i是先改变i的值再使用i的值,后置自增i++是先使用i的值然后再改变i的值。
但是在for循环中,语句3是在循环体代码执行完毕后才执行的,因此如果在循环体中没有涉及到i的运算,那么i++和++i是一样的结果。

for (语句1; 语句2; 语句3)
{
被执行的代码块
}

在循环大量数据的时候使用++i的性能要比i++性能好。原因:

i++由于是在使用当前值之后再+1,就需要一个临时变量来转存。
而++i则是直接+1,省去了对内存的操作环节,相对而言就可以提升性能。

vue路由

路由:
路由是在vue实例化的时候添加在vue实例化对象里的。

在router.js中,引入router:
import Vue from ’vue‘
import Router from ’vue-router‘ 引入路由插件

Vue.use(Router) //注册路由插件
然后导出router对象:
export default new Router({
mode:history, //html5 中的history。 也可以切换为hash
base: '/',
routes: [
{path: '', name:"", component: "" }, //

]
})

用来访问路由,用来显示所匹配访问的路由。

动态路由:
在路由里的path: '/demo1 /:id ' id表示一个参数。比如在网易云音乐里每首歌的页面地址都有一个id

meta: {title: '这是标题' } //通过meta可以进行一些更精细的配置

通过js切换路由:
在vue中可以用作为标签来点击跳转页面,但有些时候需要用js来切换。
this.$router.push('/demo5') 也可以用对象传参,里面放name

也可以使用this.$router.replace()来切换路由,参数是相同的。作用相同但有区别:
replace不会产生历史记录。
另外,router-link 标签的to方法也可以和push一样使用

子路由
在路由对象里可以嵌套子路由。

children: [
{path: '', name:"", component: "" },
]
但是子路由的path里不能使用/,因为使用/表示这是根目录里的路由

通配:

当查找不到路由时可以写一个404页面表示没有找到页面。
在路由时设置一个路由,path为”*“,name为 ”NotFound“,component设置为一个找不到页面时返回的信息页面

别名:
alias: ”/demo2666“

重定向:
给路由设置一个redirect: ’/demo2‘ , 表示重定向到这个页面。

回调函数

什么是回调函数?
A callback is a function that is passed as an argument to another function and is executed after its parent function has completed。
回调函数就是一个参数,将这个函数作为参数传到另一个函数里,当那个函数执行完之后再执行传过来的那个函数,这个过程就是回调。

举个例子说明什么是回调函数:约会结束后你送你女朋友回家,离别时,你肯定会说:“到家了给我发条信息,我很担心你。” 对不,然后你女朋友回家以后还真给你发了条信息。小伙子,你有戏了。其实这就是一个回调的过程。你留了个参数函数(要求女朋友给你发条信息)给你女朋友,然后你女朋友回家,回家的动作是主函数。她必须先回到家以后,主函数执行完了,再执行传进去的函数,然后你就收到一条信息了。

8.ES6-Class

class创建一个类(构造函数).实际上是原型链继承的语法糖。

class Name {
construct (name,age ){
this.age = age;
this.name = name;
}
}

construct接收传递来的参数同时返回实例对象。用new的时候就会调用construct

方法直接写到类里,不需要写function,函数间不能添加逗号。

子类用extends来继承父类,用super关键字来继承父类中的方法。super()写在子类的construct里,这样super里的参数可以传到父类的方法里,即实际上是用super调用父类的construct(构造器)。

super.父类方法() 就是子类调用父类中的方法

super必须放在子类this之前

类没有类的提升,因此必须先生命类才能对类实例化。
类里共有的属性和方法一定要用this使用。

Vue组件化**

先留个坑,慢慢想。
组件首先要分为可复用和不可复用的情况。某些组件只在特定页面中展现一次,那么这种一次性组件间可以有紧密的耦合。另一种情况是需要重复使用的组件,这种组件为了能更好的用在项目的任何地方中,需要尽量的独立,达到高内聚低耦合的目的。因此更应该关注的就是可复用组件的封装了。本文接下来所提到的组件特指可复用组件。

组件应当定义一个清晰的公开接口,同时也不要对其使用的外层数据作出任何假设。Vue组件的API来自三部分:prop,事件和插槽。

首先要完成界面部分的内容。

Cesium 坐标系统

Cesium中主要包括了以下几种坐标系统:

1.Cartesian3 笛卡尔空间直角坐标系。又称世界坐标。
new Cesium.Cartesian3(x,y,z)
表示以地球椭球体中心为原点的空间直角坐标系中的一点

2.Cartographic 由经纬度和高程确定某一点。经纬度都是由弧度表示。
new Cesium.Cartographic(longitude, latitude, height)

Cesium中没有具体的直接以经纬度表达的对象,需要转换为弧度或空间直角坐标来使用。

相互转换:

经纬度转笛卡尔空间坐标:
可以直接转换:
Cesium.Cartesian3.fromDegrees(longitude, latitude, height, elliposoid, result)
或者先把经纬度转为弧度,再由弧度转笛卡尔

let ellipsoid = viewer.scene.globe.ellipsoid;
let cartographic = Cesium.Cartographic.fromDegrees(lng,lat,alt);
let cartesian3 = ellipsoid.cartographicToCartesian(cartographic)

世界坐标转换为经纬度

var ellipsoid=viewer.scene.globe.ellipsoid;
var cartesian3=new Cesium.cartesian3(x,y,z);
var cartographic=ellipsoid.cartesianToCartographic(cartesian3);
var lat=Cesium.Math.toDegrees(cartograhphic.latitude);
var lng=Cesium.Math.toDegrees(cartograhpinc.longitude);
var alt=cartographic.height;

同理,得到弧度还可以用
Cartographic.fromCartesian

弧度和经纬度
经纬度转弧度:
Cesium.CesiumMath.toRadians(degrees)

弧度转经纬度:
Cesium.CesiumMath.toDegrees(radians)

屏幕坐标和世界坐标相互转换
屏幕坐标转世界坐标:

var pick1= new Cesium.Cartesian2(0,0);
var cartesian = viewer.scene.globe.pick(viewer.camera.getPickRay(pick1),viewer.scene);

这里的屏幕坐标一定是在地球上,否则会返回undefined

世界坐标转屏幕坐标:
Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, Cartesian3);

结果是Cartesian2对象。

火星坐标,84坐标,百度地图坐标相互转换

//定义一些常量
var x_PI = 3.14159265358979324 * 3000.0 / 180.0;
var PI = 3.1415926535897932384626;
var a = 6378245.0;
var ee = 0.00669342162296594323;

/**
 * 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换
 * 即 百度 转 谷歌、高德
 * @param bd_lon
 * @param bd_lat
 * @returns {*[]}
 */
function bd09togcj02(bd_lon, bd_lat) {
    var x_pi = 3.14159265358979324 * 3000.0 / 180.0;
    var x = bd_lon - 0.0065;
    var y = bd_lat - 0.006;
    var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
    var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
    var gg_lng = z * Math.cos(theta);
    var gg_lat = z * Math.sin(theta);
    return [gg_lng, gg_lat]
}

/**
 * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换
 * 即谷歌、高德 转 百度
 * @param lng
 * @param lat
 * @returns {*[]}
 */
function gcj02tobd09(lng, lat) {
    var z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI);
    var theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI);
    var bd_lng = z * Math.cos(theta) + 0.0065;
    var bd_lat = z * Math.sin(theta) + 0.006;
    return [bd_lng, bd_lat]
}

/**
 * WGS84转GCj02
 * @param lng
 * @param lat
 * @returns {*[]}
 */
function wgs84togcj02(lng, lat) {
    if (out_of_china(lng, lat)) {
        return [lng, lat]
    }
    else {
        var dlat = transformlat(lng - 105.0, lat - 35.0);
        var dlng = transformlng(lng - 105.0, lat - 35.0);
        var radlat = lat / 180.0 * PI;
        var magic = Math.sin(radlat);
        magic = 1 - ee * magic * magic;
        var sqrtmagic = Math.sqrt(magic);
        dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
        dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
        var mglat = lat + dlat;
        var mglng = lng + dlng;
        return [mglng, mglat]
    }
}

/**
 * GCJ02 转换为 WGS84
 * @param lng
 * @param lat
 * @returns {*[]}
 */
function gcj02towgs84(lng, lat) {
    if (out_of_china(lng, lat)) {
        return [lng, lat]
    }
    else {
        var dlat = transformlat(lng - 105.0, lat - 35.0);
        var dlng = transformlng(lng - 105.0, lat - 35.0);
        var radlat = lat / 180.0 * PI;
        var magic = Math.sin(radlat);
        magic = 1 - ee * magic * magic;
        var sqrtmagic = Math.sqrt(magic);
        dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
        dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
        mglat = lat + dlat;
        mglng = lng + dlng;
        return [lng * 2 - mglng, lat * 2 - mglat]
    }
}

function transformlat(lng, lat) {
    var ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
    ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;
    ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;
    return ret
}

function transformlng(lng, lat) {
    var ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
    ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0;
    ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0;
    return ret
}

/**
 * 判断是否在国内,不在国内则不做偏移
 * @param lng
 * @param lat
 * @returns {boolean}
 */
function out_of_china(lng, lat) {
    return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false);
}


Java基础

Java基础语法

大小写敏感 Java是大小写敏感的
类名 所有类名的首字母应该大写。如果类名由若干个单词组成,那么每个单词首字母都应该大写(大驼峰)
方法名 所有方法名都应该小写字母开头,多个单词则后边的单词开头大写(小驼峰)
源文件名 源文件名必须和类名相同,文件后缀为.java

标识符
类名、变量名、方法名都被称为标识符

  • 所有的标识符都应该以字母(A-Z 或者 a-z),美元符($)、或者下划线(_)开始
  • 首字符之后可以是字母(A-Z 或者 a-z),美元符($)、下划线(_)或数字的任何字符组合
  • 关键字不能用作标识符
  • 标识符是大小写敏感的
  • 合法标识符举例:age、$salary、_value、__1_value
  • 非法标识符举例:123abc、-salary

修饰符
Java可以用修饰符来修饰类中的方法和属性,主要有两类修饰符:

  1. 访问控制修饰符:default, public, protected, private
  2. 非访问控制修饰符:final, abstract, static, synchronized

Java变量主要有以下几种类型

  1. 局部变量
  2. 类变量(静态变量)
  3. 成员变量(非静态变量)

继承
一个类可以由其它类派生。利用继承的方法,可以重用已存在的类的方法和属性。派生类为子类,被继承的类叫超类

接口
Java中的接口可理解为对象间相互通信的协议。接口只定义派生要用到的方法,但方法的具体实现完全取决于派生类

5.面向对象与继承

面向对象编程(OOP)特征

  • 封装:高内聚低耦合
  • 继承:子类继承父类中的属性和方法
  • 多态:重写和重载。JS不存在真正的重载。

JS继承实现方法

  1. 原型继承
  2. call继承
  3. 寄生组合继承
  4. ES6 的Class继承,extends和super

继承方案

一、原型继承:
让父类的属性和方法在子类的原型链上。

Child.prototype = new Parent();
Child.prototype.constructor = Child;
  1. 基于原型链查找这个机制完成的继承,不是简单的拷贝继承。
  2. 子类可以重写父类的方法,会导致父类创建的实例也受到影响
  3. 父类中公有2或私有的属性和方法都会成为子类公有的属性和方法

二、call继承
Child方法把Parent作为普通函数执行,让Parent中的this指向Child的实例,相当于给Child的实例创建了很多私有的属性方法。

  1. 只能继承父类私有的属性或方法(因为是把Parent作为普通函数执行,和其原型上的属性和方法没有关系)
  2. 父类私有的变成子类私有

三、寄生组合继承
Call继承+原型继承。

父类私有变子类私有,父类公有变子类公有。

四、ES6 Class

vue状态管理

当项目里需要频繁大量的进行数据共享时,使用vuex。
使用vuex可以在整个项目里更高效的实现数据通信和共享。并且存储在vuex中的数据都是响应式的,能让数据和页面保持同步。

一般当组件数据需要共享时就存储到vuex中,组件的私有数据一般就在组件的data里。

内网,外网,局域网,广域网的概念

关于内网和外网

划分内网和外网的重要依据主要是判断它是不是与广阔的外界相连。这两个概念也是模糊且相对的。
简单的说,自己的单位或者家庭、小区内部有局域网;单位、家庭之外有覆盖范围极大的网络,比如internet,这个大网络延伸到了我们的单位、家庭(通过光纤、网线、电话线等)。我们把自己的局域网连接到internet上,那么我们的访问范围就从局域网扩展到了整个internet。这时候,就说局域网是内网,internet是外网。

同理,如果你们单位的局域网很庞大,而你的办公室里面的几台电脑组成的小局域网又连接到单位的整个大局域网,那么也可以说单位的大局域网是外网,办公室内的小局域网是内网。同时,如果单位的大局域网连接了Internet,那么相对于Internet,也可以说单位的大局域网是内网。

内网可能是一个独立的局域网,通过其中的网关(网关就是连接两个网络的节点,说白了,就是有双重身份的电脑,既有局域网的IP地址,又有Internet的IP地址,两个IP地址分别捆绑在不同的网卡上)的代理访问外部网络,比如网吧都是这样实现的,其特征是:网吧内的电脑的ip都是局域网专用ip,比如192.168.xxx.xxx或者10.xxx.xxx.xxx,而这种ip在internet上面是不会出现的。

(注:所谓代理,就是你提要求,他来办事,类似于代购火车票。局域网的电脑想和外面联络,就把对方地址告诉服务器,也就是网关,网关以自己的身份和对方联络,同时把对方发回来的消息转送给局域网内的电脑。因此,对方看不见局域网内电脑的IP,只会以为是网关那台电脑在与自己交流。网吧内的所有QQ都显示同样的IP,现在你能理解为什么了吗?)

内网也可能是外网的一个部分,比如校园网,或者相对于单位局域网的办公室内部局域网。其特征是:内网电脑的ip就是整个外网ip范围的一部分,内网的电脑通过网关(路由器)连接到外网,网关不需要进行代理服务,直接路由就行了。

(注:所谓路由,就是路径选择。路由器连接多个网络,因此一定是各个网络的网关,其作用类似于邮局。你想联系局域网外的电脑,就把邮包发送给路由器,路由器会帮你投递到邮包上标明的地址。这样,收到邮包的人可以知道是谁把邮包发过来的,但是,他无法知道发邮包的人是不是帮别人代理发邮包的。或者说,收到数据的电脑可以知道是哪台电脑在与它联络,但它无法知道与它联络的这台电脑是否是某个局域网的代理服务器。)

adsl比较特殊,它有两种工作方式。第一种,adsl的modem打开代理功能,这时候,modem实际上就可以看作一台电脑,它是internet(外网)的一个节点,同时,它与你的电脑连接成为局域网,也就是内网,内网网关就是modem。第二种,通过电脑进行拨号上网,这种情况下,modem就是电脑的一个外部设备,而你的电脑通过电话线直接连接在internet上,不存在其它网络,因此也就无所谓内网外网。

关于广域网(WAN,公网,外网)和局域网(LAN,私网,内网)

广域网(WAN),就是我们通常说的Internet,它是一个遍及全世界的网络。
局域网(LAN),相对广域网而言,主要是指在小范围内的计算机互联网络。这个小范围可以是一个家庭,一个学校,一个部门,一个公司等。
BT中常常提到的公网,外网即广域网,BT中常提到的私网,内网即局域网。

广域网上每台电脑(或其它网络设备)都有一个或多个广域网IP地址(或者说公网,外网IP地址),广域网IP地址一般要到ISP处交费才能申请到,广域网IP地址不能重复。局域网上每一台电脑(或其它网络设备)都有一个或多个局域网IP地址(或者说私网,内网IP地址),局域网IP地址是局域网内部分配的,不同局域网IP地址可以重复,不会相互影响。

广域网(WAN,公网,外网)与局域网(LAN,私网,公网)电脑交换数据要通过路由器或网关的NAT(网络地址转换)进行。一般来说,局域网内电脑发起的对外连接请求,路由器或网关都不会加以阻拦,但来自广域网内电脑连接的请求,路由器或网关在绝大多数情况下都会进行拦截。

通常情况下,网关或路由器对内部向外发出的信息不会进行拦截,但对来自外部想进入内部网络的信息则会进行识别、筛选,认为是安全的、有效的,才会转发给内网电脑。正是这种情况的存在,才导致了很多内网用户没有“远程”,速度也不尽如人意。

NAT,网络地址转换,主要功能是可以在内网配置私有IP地址,然后在路由或者防火墙等边界设备上统一转换为公网地址访问互联网,这样就不用为局域网内每台终端设备配置公网IP地址,解决了IPV4地址枯竭问题。

JS文件下载和上传

下载文件

1.直接使用标签

<a href="../../static/xxx.xlsx" download="xxx.xlsx">下载</a>
不加download属性时,如果文件格式为txt, pdf, jpg等浏览器支持直接打开的文件时就不会下载,而是直接打开。添加download属性后,下载文件名默认为属性值。

2.使用window.open()

window.open("../../static/xxx.xlsx")
window.open("https://mp.csdn.net/postedit/static/xxx.xlsx")

TIM图片20200717100135
下载文件的另一种方式,js添加标签完成下载

上传文件

1.原生js实现

<input id="file_upload" type="file" name="testfile">
<button id="js_post">原生js提交</button>

document.getElementById("js_post").onclick=function(){
    var xml=new XMLHttpRequest();
    var data=new FormData; //创建formdata对象
    data.append("testfile",document.getElementById("file_upload").files[0]);//找到对象之后的file[0]对应的就是文件对象
    xml.open("POST","/test/",true);
    xml.onreadystatechange=function(){
        if(xml.readyState==4 && xml.status==200){  //判断状态到4了并且返回状态码是200时才做操作
            var rsp_data=JSON.parse(xml.responseText);  //反序列化
            if (rsp_data.state){
                var url="/"+rsp_data.data;  //拼接路径
                var obj=document.createElement("img");  //创建标签
                obj.className="show-img";  //给标签加样式
                obj.src=url;  //给标签加url
                document.getElementById("imgs").appendChild(obj);
            }
        }
    };
    xml.send(data)
}

2.Vue中使用axios上传文件

<input type="file" ref="fileInt" @change="changeHandle">

changeHandle() {
      const file = this.$refs.fileInt.files[0];
      console.log(file);
      const data = new FormData();
      data.append('file', file);
      axios.post('http://localhost:3006/common/upload', data, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      }).then(res => {
        console.log(res);
      }).catch(err => {
        console.log(err);
      });
    }

WMTS地图切片原理

1.定义

地图切片:采用预生成的方法存放在服务器端,然后根据用户提交的不同请求,把相应的地图瓦片发送给客户端的过程,它是一种多分辨率层次模型,从瓦片金字塔底层到顶层,分辨率越来越低,但表示的地理范围不变。

地图缓存:又称地图瓦片,是一个包含了不同比例尺下整个地图范围的地图切片的目录,即一个缓存的地图服务就是能够利用静态图片来快速地提供地图的服务。

2.优缺点
1)速度快,预先配色,显示效果佳,满足高并发访问,适合做底图。

2)瓦片地图缓存非常高效,如果你曾经查看过某一区域的瓦片,当你再次查看该区域的瓦片时,浏览器可能使用之前缓存的相同的瓦片,而不用重新下载一次。

3)瓦片地图可以渐进加载。中心区域的瓦片可能优先于边缘区域的瓦片加载,你可以移动或定位某一点,即使当前地图边缘区域还未加载。

4)简单易用。所以很容易在服务器、网络、桌面或移动设备上实现技术集成。

5)需要额外占用磁盘空间,需要预先生成切片,无法自定义地图。

3.原理

切片之前要确定切图的起点(Tiling scheme origin point)、图片宽度和高度,设置起点(WGS84下默认是(-400,400))的目的在于保证同一坐标系下的切片地图可以完美的叠加在一起。

在默认的Tiling scheme origin下和图片大小的情况下进行地图切片,可以根据用户设置的比例尺来计算地图数据在整个网格中的行列号,计算公式如下:

col=(int)Math.floor((point.x1-this.x1)/256/plottingResolution);
row=(int)Math.floor((Math.abs(point.y1-this.y1))/256/plottingResolution);

其中point是当前坐标,this是origin点坐标,plottingResolution指当前Level的地图的地图分辨率。

如果要计算plottingResolution,可以根据用户输入的levelScale得到当前条件下的地图分辨率:

Scale=1:(96*2*Math.PI*6378137*resolution/360/0.0254);

4.主要概念

  1. 四至(Extent)和原点(Origin)

2)切片的分辨率和比例尺

比例尺:图上一单位长度代表实际多少长度单位;分辨率:屏幕一像素(px)代表实际多少单位,实际单位按照地图坐标而定。分辨率与dpi(每英寸的像素数)有关、与地图单位有关。

分辨率和比例尺转换关系:
1、如果地图单位是米,dpi=96 1英寸=2.54厘米;1英寸=96像素;最终换算的单位是米;

Scale=1:(96*Resolution/0.0254);
2、如果地图单位是度(地理坐标系是WGS84),dpi为96,1度约等于111194.872221777米;

Scale=1:(962Math.PI6378137Resolution/360/0.0254);

5.切片分类

1)矢量切片(详情)

是一种利用协议缓冲(Protocal Buffers)技术的紧凑的二进制格式用来传递信息。当渲染地图时矢量切片使用一系列存储的内部数据进行制图。被组织到矢量切片的图层比如(水、道路、区域等),每一层都有包含几何图形和可变属性的独立要素。通俗地讲,就是将矢量数据以建立金字塔的方式,像栅格切片那样分割成一个个描述性文件,以GeoJson格式或者PDF等自定义格式组织,然后在前端根据显示需要按需请求不同的矢量瓦片数据进行Web绘图

常见的格式有GeoJson、TopoJson、PDF(ArcGIS格式)、KML、GeoRSS等等。

2)栅格切片

Cesium-Scene

Cesium中的Cartesian直角坐标系以地球球心为原点,x轴是朝向本初子午线和赤道交点,y轴是朝向亚洲赤道部分,z轴是朝着北极。

在使用Scene加载3d model时,要指定它的modelMatrix。一个四阶矩阵。通过这个来确定位置。modelMatrix通过Cesium.Transforms.headingPitchRollToFixedFrame(origin, hpr)来构造。heading左右角度,pitch是上下,都是相对于自身。朝东的方向是相对的初始方向。参数hpr是由HeadingPitchRoll这个类构造的,origin是三维空间直角坐标值,Cartesian3类创造。包括xyz值。如果知道经纬度和高度,可以通过Cesium.Cartesian3.fromDegrees()方法转换为空间坐标值。
之后使用scene.primitives.add(Cesium.Model.fromGltf({
url: url,
modelMatrix: modelMatrix,
minimumPixelSize: 128
}))来创建引入。
对于这些需要加载的资源,不可能引入就直接显示。因此基本上都需要等待加载完毕,使用model.readyPromise.then(function(){})
进行后续操作。

关于事件ScreenSpaceEventHandler:这是封装好的事件类。使用时需要创建这个类的实例。
如:
let handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction(function(movement){
let pick = scene.pick(movement.endPosition) // 拾取鼠标当前坐标
},Cesium.ScreenSpaceEventType.MOUSE_MOVE )
以上代码表示当鼠标移动时执行回调函数。

在Cesium事例中,所有的自定义控件都是通过knockout来操作的。Cesium.knockout监听html页码控件中数值等的变化。
首先创建一个viewModel对象,里面存放的是监听控件中数值的属性。在控件的HTML中使用data-bind来和viewModel中的属性对应。
之后使用Cesium.knockout.track(viewModel);来监听控件里数值变化,
var toolbar = document.getElementById('toolbar');
Cesium.knockout.applyBindings(viewModel, toolbar); 这一步是绑定控件DOM和viewModel中的属性。
Cesium.knockout.getObservable(viewModel, "fill").subscribe(function(value) {
polygon.fill=value
});
通过getObservable来进行后续操作,当控件中的值改变时进行回调函数中的操作。

webpack配置

loader不需要引入,plugins需要引入配置文件

提取css为单独文件:

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

使用mini-css-extract-plugins:

test: /.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]

plugins: {

new MiniCssExtractPlugin({
filename: 'src/built.css'
})
}

css兼容性处理:

use : [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'post-css-loader',
options: {
ident: 'postcss',
plugins: ()=>{
require('post-preset-env' )()
}
}

}

]

为了css浏览器兼容性,在package.json 中使用browerslist

"browserslist": {
"development": [ 'last 1 chrome version' , 'last 1 safari version' ],
"production": [ ">0.2%", "not dead", "not op_mini all" ]
}

压缩css

使用插件OptimizeCssAssetsWAssetsWebpackPlugin

Cesium贴地面积计算(未经验证)

viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
    //绘制点
    function createPoint(worldPosition) {
        console.log(worldPosition.x);
        if (worldPosition.x != NaN && worldPosition.y != NaN && worldPosition.z != NaN) {
            var point = viewer.entities.add({
                position: worldPosition,
                name: 'pointer',
                point: {
                    color: Cesium.Color.WHITE,
                    pixelSize: 10,
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
                }
            });
            return point;
        }

    }
    //绘制图形
    function drawShape(positionData) {
        var shape;
        //当positionData为数组时绘制最终图,如果为function则绘制动态图
        var arr = typeof positionData.getValue === 'function' ? positionData.getValue(0) : positionData;
        var ceshi = new Cesium.CallbackProperty(function () {
            var obj = new Cesium.PolygonHierarchy(arr);
            console.log(obj.positions)
            return obj.positions;
        }, false);
        console.log(typeof ceshi);
        shape = viewer.entities.add({
            polygon: {
                hierarchy: arr,
                material: new Cesium.ColorMaterialProperty(Cesium.Color.ORANGE.withAlpha(0.3)),
            }
        })
        return shape;
    }
    var activeShapePoints = [];
    var activeShape;
    var floatingPoint;
    var handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
    handler.setInputAction(function (event) {
        // We use `viewer.scene.pickPosition` here instead of `viewer.camera.pickEllipsoid` so that
        // we get the correct point when mousing over terrain.
        var earthPosition = viewer.scene.pickPosition(event.position);
        // `earthPosition` will be undefined if our mouse is not over the globe.
        if (Cesium.defined(earthPosition)) {
            if (activeShapePoints.length === 0) {
                floatingPoint = createPoint(earthPosition);
                activeShapePoints.push(earthPosition);
                var dynamicPositions = new Cesium.CallbackProperty(function () {
                    return activeShapePoints;
                }, false);
                activeShape = drawShape(dynamicPositions);
            }
            activeShapePoints.push(earthPosition);
            createPoint(earthPosition);
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

    handler.setInputAction(function (event) {
        if (Cesium.defined(floatingPoint)) {
            var newPosition = viewer.scene.pickPosition(event.endPosition);
            if (Cesium.defined(newPosition)) {
                floatingPoint.position.setValue(newPosition);
                activeShapePoints.pop();
                activeShapePoints.push(newPosition);
            }
        }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    // Redraw the shape so it's not dynamic and remove the dynamic shape.
    function terminateShape() {
        activeShapePoints.pop();
        drawShape(activeShapePoints);
        var lastRectangle = drawShape(activeShapePoints);//绘制最终图
        var coors = lastRectangle._polygon._hierarchy._value;
        var Geographys = [];
        for (var i = 0; i < coors.length; i++) {
            Geographys.push({ lon: Number(Cesium.Math.toDegrees(viewer.scene.globe.ellipsoid.cartesianToCartographic(coors[i]).longitude)), lat: Number(Cesium.Math.toDegrees(viewer.scene.globe.ellipsoid.cartesianToCartographic(coors[i]).latitude)) })
        }
        //Geography = [Cesium.Math.toDegrees(Geography.longitude), Cesium.Math.toDegrees(Geography.latitude)];
        console.log(lastRectangle, coors, Geographys, "duobianxing贴面")
        viewer.entities.remove(floatingPoint);
        viewer.entities.remove(activeShape);
        floatingPoint = undefined;
        activeShape = undefined;
        activeShapePoints = [];
        return Geographys;
    }
    handler.setInputAction(function (event) {
        var pointer = terminateShape();
        viewer.scene.globe.depthTestAgainstTerrain = false;
        handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
        console.log(computeSignedArea(pointer).toFixed(1) / 1000000, pointer[0].lon, pointer[0].lat)
        var mj = (computeSignedArea(pointer) / 1000000).toFixed(1);
        viewer.entities.add({
            position: Cesium.Cartesian3.fromDegrees(pointer[0].lon, pointer[0].lat),
            label: {
                text: mj + 'k㎡',
                font: '22px Helvetica',
                fillColor: Cesium.Color.SKYBLUE,
                outlineColor: Cesium.Color.BLACK,
                outlineWidth: 2,
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND 
            }
        });
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    //计算多边形面积
    function computeSignedArea(path) {
    let radius = 6371009
    let len = path.length;
    if (len < 3) return 0;
    let total = 0;
    let prev = path[len - 1];
    let prevTanLat = Math.tan(((Math.PI / 2 - prev.lat / 180 * Math.PI) / 2));
        let prevLng = (prev.lon) / 180 * Math.PI;
    for (let i in path) {
        let tanLat = Math.tan((Math.PI / 2 -
            (path[i].lat) / 180 * Math.PI) / 2);
        let lng = (path[i].lon) / 180 * Math.PI;
        total += polarTriangleArea(tanLat, lng, prevTanLat, prevLng);
        prevTanLat = tanLat;
        prevLng = lng;
    }
    return Math.abs(total * (radius * radius));
    }
    function polarTriangleArea(tan1, lng1, tan2, lng2) {
        let deltaLng = lng1 - lng2;
        let t = tan1 * tan2;
        return 2 * Math.atan2(t * Math.sin(deltaLng), 1 + t * Math.cos(deltaLng));
    }
    //清楚图层并且注销事件
    function clearAlls() {
        var clearS = viewer.entities.values;
        console.log(clearS);
        viewer.scene.globe.depthTestAgainstTerrain = false;
        handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
        for (var i = 0; i < clearS.length; i++) {
            viewer.entities.removeById(clearS[i]._id)
            i--;
        }
        console.log(clearS)
        getdata_ZHDPage();
    }

1.执行上下文

执行上下文概念:当前JavaScript代码被解析和执行时所在环境。任何JavaScript代码都要在执行上下文中运行。

类型

  1. 全局执行上下文:默认最基础的执行上下文。不在任何函数中的代码都在全局执行上下文中。
    它做了两件事:创建一个全局对象(浏览器中就是window);将this指针指向这个全局对象。一个程序只存在一个全局执行上下文。

  2. 函数执行上下文:当每次调用函数时都会为该函数创建一个新的函数执行上下文。每个函数都有自己的执行上下文,但只有调用时才会被创建。

  3. Eval函数执行上下文:运行在eval函数里的代码也有自己的执行上下文。由于用得少,不再详细说明。

执行栈
在其他语言中叫调用栈。有后进先出的结构,用于存储代码执行期间创建的所有执行上下文。

当js引擎首次读取你的脚本时,它会创建一个全局执行上下文并将其推入当前执行栈,每当发生一个函数调用,引擎都会为该函数创建一个新的执行上下文并将其推入当前执行栈的顶端。
引擎会运行执行上下文在执行栈顶端的函数,当此函数运行完后,它对应的执行上下文会从执行栈中弹出,上下文控制权移到下一个执行上下文。

执行上下文如何创建
执行上下文分为两个阶段创建:

一、创建阶段
在任意js代码被执行前,执行上下文处于创建阶段。在创建阶段共发生了三件事:

  1. 确定this的值(this binding)
    在全局执行上下文中this指向全局对象(浏览器中就是window);
    在函数执行上下文中,this的指向取决于函数的调用方式。如果它被一个对象引用,this值被设置为该对象,否则this会指向全局对象或undefined。

  2. 词法环境组件被创建
    词法环境是一个包含标识符变量(变量名/函数名)映射的结构。由环境记录和对外部环境的引用组成。环境记录是存储变量和函数声明的实际位置,对外部环境的引用意味着它可以访问其外部词法环境。
    词法环境包括全局环境和函数环境。全局环境没有外部环境,全局环境的外部环境引用为null。函数环境,用户在函数中定义的变量被存储在环境记录中,对外部环境的引用可以是全局环境或包含内部函数的外部函数环境。对于函数环境而言,环境记录包含一个arguments对象。

环境记录有两种类型:声明性环境记录,用于存储变量,函数和参数。一个函数环境包含声明性环
境记录;对象环境记录,用于定义在全局执行上下文中出现的变量和函数的关联,全局环境包含对
象环境记录。

  1. 变量环境组件被创建
    在ES6中,词法环境和变量环境的区别在于前者用于存储函数声明和变量(let和const)绑定,而后者仅用于存储变量(var)绑定

在创建阶段,代码会被扫描并解析变量和函数声明,其中函数声明存储在环境中,而变量会被设置为undefined(var)或未初始化(let和const)。因此var存在变量提升,而let和const存在暂时性死区。

二、执行阶段
完成对所有变量的分配,然后执行代码。

Node.js

node是js的一个执行环境。它可以在浏览器外运行,在V8引擎上运行。node.js是单线程的,在标准库里提供了一组异步的I/O原生功能以防止代码阻塞。当Node.js执行I/O操作(如从网络读取,访问数据库或文件系统)时会在响应返回时恢复操作而不是阻塞线程。

node.js程序示例:

const http = require("httpp")
const hostname = '127.0.0.1'
const port  = 3000
const server = http.createServer((req, res)=> {
  res.statusCode = 200;
  res.setHeader('Content-Type','text/plain')
  res.end('Hello, World')
})
server.listen(port, hostname, ()=> {
  console.log(`服务器运行在http: //${hostname}:${port}`)
})

这段代码首先引入了http模块,然后创建了新的HTTP服务器并返回。服务器被设置为监听指定的端口和主机名。服务器就绪后就会调用回调函数,并打印出服务器运行的提示。
每当接收到新的请求时,request事件就会被调用,并提供两个对象: 一个请求(http.IncomingMessage 对象)和一个响应(http.ServerResponse 对象)
请求对象提供了请求的详细信息,响应对象用于返回数据给调用方。
res.end('Hello, World') 表示关闭响应,并添加内容。

从命令行中运行node.js
运行全局可用的node命令。
node app.js
则会执行app.js这个文件。

结束运行
当运行到process.exit()时,进程会被立即强制终止。process不需要require。
可以传入一个整数向操作系统发送退出码。如process.exit(1)
默认情况下退出码为0,表示成功。不同退出码有不同含义,可以用于与其它程序的通信。
退出码详情

使用node启动服务器,这种程序永远都不会结束。如果这种情况下调用process.exit()就不太好。

const express = require('express')
const app = express()
app.get('/', (req, res) => {
res.send('你好')
})
app.listen(3000, () => console.log('服务器已就绪'))

这种情况下需要向该命令发送SIGTERM(终止进程,程序结束)信号,并使用进程信号处理程序进行处理:

const express = require('express')
const app = express()
app.get('/', (req, res) => {
  res.send('你好')
})
const server = app.listen(3000, () => console.log('服务器已就绪'))
process.on('SIGTERM', () => {
  server.close(() => {
    console.log('进程已终止')
  })
})

信号是一个POSIX内部通信系统: 发送通知给进程以告知其发生的事件。
SIGKILL是告诉进程要立即终止的信号。理想情况下类似于process.exit()
SIGTERM是告诉程序要正常终止的信号。它是进程管理者(如upstart或supervisord)等发出的信号。
可以从程序内部另一个函数中年发送此信号:
process.kill(process.pid, 'SIGTERM')

读取环境变量
process核心模块提供了env属性,该属性承载了在启动进程时设置的所有环境变量。这是访问NODE_ENV环境变量的示例,默认情况下被设置为development
process.env.NODE_ENV

打印堆栈踪迹:
console.trace()

如下代码就会打印堆栈踪迹:

const function2 = ()=> console.trace()
const function1 = ()=> function2()
function1()

从命令行接收输入

Node.js 提供了 readline 模块来执行以下操作:每次一行地从可读流(例如 process.stdin 流,在 Node.js 程序执行期间该流就是终端输入)获取输入。

const readline = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout
})

readline.question(`你叫什么名字?`, name => {
  console.log(`你好 ${name}!`)
  readline.close()
})

这段代码会询问用户名,当输入了文本并且用户按下回车键时,则会发送问候语。question() 方法会显示第一个参数(即问题),并等待用户的输入。 当按下回车键时,则它会调用回调函数。
在此回调函数中,关闭了 readline 接口。

内网穿透

内网穿透,即NAT穿透,简单来说是为了让外网可以访问局域网的技术。

内网穿透作用:
1.发布应用/网站,外网可以访问。平时所做的应用开发只能在局域网本地访问,通过内网穿透,可以让全外网访问。
2.可以实现远程控制。

常用内网穿透工具
Ngrok
EchoSite
NATAPP

NATAPP只需注册就可获得免费的外网域名,不过是动态的,几个小时就会改变。

内网穿透需要做的准备
1.有固定公网IP的服务器。
2.已经备案的域名。

Vue动态组件、插槽

动态组件:可以使用<component :is= "currentComponent" /> 来指定在当前页面使用哪个组件。
比如流程中返回上一步能记住上一步做了什么。

切换组件后发现不保存切换前的组件状态,这时就使用<keep-alive></keep-alive>包裹住component,用来保存状态。

插槽(内容分发)

父组件中用了子组件标签,然后在这个标签里又插入了HTML标签用于插入内容,这个时候插入的内容就会默认出现在子组件中的<slot></slot> 中。
当需要区分插槽时子组件内使用<slot name = "name"></slot>,而父组件就在子组件的标签里插入```
<template v-slot: 'name'>
这里用来插入需要分发的内容,根据v-slot和name找到对应插槽。



JS异常处理

js中的异常处理语句有两个,一个是try……catch……,一个是throw。

try……catch用于语法错误,错误有name和message两个属性。throw用于逻辑错误。

对于逻辑错误,js是不会抛出异常的,也就是说,用try catch没有用。这种时候,需要自己创建error对象的实例,然后用throw抛出异常。

(1)try……catch……的普通使用

错误内容:charAt()小写了

try{
 var str="0123";
 console.log(str.charat(2));
}catch(exception){
    console.log("name属性-->"+exception.name);//name属性-->TypeError
    console.log("message属性-->"+exception.message);//message属性-->str.charat is not a function
}

(2)try……catch……无法捕捉到逻辑错误

错误内容:除数不能为0

try {
    var num=1/0;
    console.log(num);//Infinity
}catch(exception){
    console.log(exception.message);
}

(3)用throw抛出异常,需要自己现实例化一个error

注意:throw要用new关键字初始化一个Error,E要大写。同时,这个Error是异常里面的message属性!

try{
var num=1/0;
if(num=Infinity){
throw new Error("Error大写,用new初始化-->除数不能为0");
}
}
catch(exception){
console.log(exception.message);
}

REST和RESTful

REST全称Resource Representation State Transfer。翻译为“表现层状态转移”,通俗说是指资源在网络中以某种表现形式进行状态转移,用URL定位资源,用HTTP描述操作。
简单来说,看到URL就知道要什么,看到http method就知道干什么,看到http status code就知道结果如何。
核心**是把网页请求当成资源看待,实现如何把请求当成资源。
这是一种设计模式

REST是面向资源的,资源是通过URI进行暴露。而对资源的操作则和URI无关,因此URI中不要出现动词。
比如下边的例子,左边是错误的设计,右边是正确的设计。

GET /rest/api/getDogs --> GET /rest/api/dogs 获取所有小狗狗 
GET /rest/api/addDogs --> POST /rest/api/dogs 添加一个小狗狗 
GET /rest/api/editDogs/:dog_id --> PUT /rest/api/dogs/:dog_id 修改一个小狗狗 
GET /rest/api/deleteDogs/:dog_id --> DELETE /rest/api/dogs/:dog_id 删除一个小狗狗

HTTP动词:
GET 获取一个资源
POST 添加一个资源
PUT 修改一个资源
DELETE 删除一个资源
这就是HTTP method,对应增删改查,表示了对资源的操作。

HTTP状态码
200 OK
400 Bad Request
500 Internal Server Error
表示结果

webpack性能优化

分为
开发环境性能优化:优化打包速度,优化代码调试
生产环境性能优化:优化打包速度,优化代码运行性能

1.HMR:模块热替换(热加载)
在打包之后再修改某个模块后不会重新打包所有,而只是把变化的那部分打包了。

样式文件:可以使用HMR,因为style-loader内部实现了。
js文件:默认不能使用HMR,
解决方案:在js文件里设置,添加支持HMR功能的代码。
if(module.hot){
module.hot.accept('./print.js',function(){
//方法会监听print.js文件的变化,一旦发生变化,其他不会默认打包,会执行后面的回调函数
print();

})
}
注意:HMR只能对js非入口文件进行处理。

html文件:默认不能使用HMR且html不能热更新。(不用做HMR功能)
解决方案:修改entry入口,把html文件引入entry中。

mode: 'development',
devServer: {
contentBase: resolve(__dirname, 'build.js'),
compress: true,
port: 3000,
open: true, //开启热加载
hot: true
}

2.source-map 源代码映射
如果构建后代码出错,可以根据source-map跟踪找到源代码中的错误在哪。(错误代码的准确信息和错误位置)
在webpack.config.js中最基本的用法:

devtool: 'source-map',

[inline-|hidden-|eval-] [no-sources] [cheap-[module-] ]source-map

inline-source-map:内联,只生成一个内联source-map 错误代码的准确信息和错误位置
hidden-source-map:外部 错误代码的准确信息和错误位置
eval-source-map:内联,每个文件都生成对应的source-map 错误原因,位置则无法查找。不能追踪源代码的错误
nosource-source-map: 错误信息能找到,但是隐藏源代码
cheap-source-map:只能追踪到行

内联和外部的区别:内联没有生成新文件,内联构建速度更快

3.oneOf
正常来说,一个文件只能被一个loader处理。如果需要多个loader,则需要指定先后顺序。比如先使用eslint再用babel

oneOf用途:提升loader速度。例如一个css文件被处理之后就不会继续找下边处理js的loader。
不能有两个配置处理同类型的文件

4.缓存

babel缓存:当只有一个js文件改变时不用把没变的js文件也babel了,这时靠babel缓存。使用:
在babel-loader的配置里:cacheDirectory : true

文件资源缓存
新写一个server.js用来存放服务器代码。用node server.js 来启动服务器
在修改源代码时为了重新打包,在js和css等文件名加上哈希值,这样可以防止强制缓存带来的无法修改错误。
但是js和css使用同一个hash值,重新打包后可能导致所有缓存失效。
chunkhash.根据chunk使用哈希值,如果来源于同一个chunk,则使用相同的hash。因为css是在js中引入的,所以同属于同一个chunk。
contenthash: 根据内容创建hash.不同的文件的hash一定不一样。让代码上线运行更好用

const express = require('express')
const app = new express();

app.use(express.static( 'build', {maxAge:1000*3600 } )); //缓存一个小时
app.listen(3000) //端口是3000

5.tree shaking
树摇,摇去无用代码。前提:必须使用es6模块化,开启production环境

在package.json中设置:
”sideEffects“: false, //在所有文件都用树摇,都没有副作用。问题:可能会把css / @babel/polyfill等文件(副作用)摇掉。
”sideEffects“: [ '*.css' ] //不会对css进行树摇

6.code split 代码分割
代码分离实际上就是只加载用户需要使用到的部分代码,不是必须的就暂时不加载。
在webpack.config.js里除了入口出口loader等,配置一个
optimization:{
splitChunks: { chunks: 'all' } //将node_modules中代码单独打包输出
}
会自动分析,多个chunk中如果有公共文件,就会打包成一个chunk,不会重复打包

6.lazy loading 懒加载 和prefetch 预加载
懒加载:触发到某些条件(需要)时才会加载(js文件)把import放到一个异步函数中
预加载:会在使用前提前加载js文件。等其他资源加载完毕,浏览器空闲的时候加载
正常加载可以认为是并行加载的

7.PWA 渐进式web应用
离线可访问
workbox-webpack-plugin

const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
plugins: [
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim:true,
skipWaiting: true
})

]

要在入口文件中注册serviceworker。
if(”serviceWorker“ in navigator){
window.addEventListener('load',()=>{
navigator.serviceWorker.register('./service-worker.js').then(()=>{ console.log('成功') }).catch(()=>{ console.log('失败')})
})
}

必须运行在服务器上

8.多进程打包

thread-loader
在js文件中use :thread-loader,开启多进程打包。
进程启动和通信需要时间,因此只有工作时间长的时候才需要。

需要调整的时候可以把thread-loader改成一个对象:
{
loader: 'thread-loader',
options: {
workers: 2, //指两个进程
}
}

9.externals
防止某些文件被打包
在modules.exports = {
externals: {
jquery: 'jQuery', //忽略包名 --npm包。 拒绝jQuery被打包进来,这样就可以手动引进jQuery的
// CDN
}

}

10. dll 动态链接库(过于复杂,尚未完成)
使用dll,对某些库(jQuery,react,vue第三方包)进行单独打包
运行webpack时,默认查找webpack.config.js
当需要使用dll时,webpack --config webpack.dll.js

webpack.dll.js

const {resolve }= require('path');
const webpack = require('webpack');

modules.exports = {
entry: {
jquery: [ 'jquery' ] // 最终生成的[name] ----jquery, [ 'jquery' ] ----要打包的库是jQuery
},
output:{
filename: ”[name].js“,
path : resolve(__dirname,'dll'),
library: '[name][hash]' //打包的库里向外暴露的内容叫什么名字
},
plugins:[
//打包生成一个manifest.json文件,用来提供和jQuery的映射关系
new webpack.DllPlugin({
name: '[name]
[hash]', //映射出库的暴露内容的名字
path: resolve(__dirname, "dll/manifest.json") //输出文件路径
})
]

}

webpack配置

loader不需要引入,plugins需要引入配置文件

打包其他资源

rules:[
{
test: '/.css$/',
use: [style-loader,css-loader]
},
{
exclude: /.(js|css|html|less)$/, //排除css js html资源
loader: file-loader //打包其他资源
options: { name : '[hash:10].[ext]' }
}
]

开发服务器:devServer,用来自动化(自动编译,自动打开浏览器,自动刷新浏览器)
包:webpack-dev-server

module.exports = {
...
...
devServer: {
特点:只在内存中自动编译打包,不会由任何输出。
contentBase:resolve(__dirname,'build') ,
compress: true, //用gzip压缩
port: 3000, //端口号
open: true //默认打开默认浏览器
}

}

Java面向对象

Java继承

在Java中通过extends关键字可以申明一个类是从另一个类继承来的。
class 父类 {
}
class 子类 extends 父类 {
}

Java不支持多继承(指一个子类继承一个以上的父类),但支持多重继承。

继承的特性
子类拥有父类非private的属性,方法
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展
子类可以用自己的方式实现父类的方法
Java的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性,C++是支持多继承的。
继承的缺点是提高了类之间的耦合性。

继承关键字
可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang 包中,所以不需要 import)祖先类。

extends关键字
在Java中,类的继承是单一继承,因此extends只能继承一个类。

public class Animal { 
    private String name;   
    private int id; 
    public Animal(String myName, String myid) { 
        //初始化属性值
    } 
    public void eat() {  //吃东西方法的具体实现  } 
    public void sleep() { //睡觉方法的具体实现  } 
} 
 
public class Penguin  extends  Animal{ 
}

implements关键字
使用implements关键字可以变相的是Java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口和接口之间采用逗号分隔)

public interface A {
    public void eat();
    public void sleep();
}
 
public interface B {
    public void show();
}
 
public class C implements A,B {
}

super与this关键字
super关键字:可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类
this关键字:指向自己的引用。

class Animal {
  void eat() {
    System.out.println("animal : eat");
  }
}
 
class Dog extends Animal {
  void eat() {
    System.out.println("dog : eat");
  }
  void eatTest() {
    this.eat();   // this 调用自己的方法
    super.eat();  // super 调用父类方法
  }
}
 
public class Test {
  public static void main(String[] args) {
    Animal a = new Animal();
    a.eat();
    Dog d = new Dog();
    d.eatTest();
  }
}

final关键字
final关键字声明类可以把类定义为不能继承的,即最终类,或者用于修饰方法,该方法不能被子类重写

声明类: final class 类名{ }
声明方法:修饰符(public/private/default/protected)final 返回值类型 方法名(){ }

构造器
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类构造器带函数,则必须在子类构造器中显式地通过super关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用super关键字调用父类构造器,系统会自动调用父类的无参构造器。

Java重写(Override)与重载(Overload)

重写(Override)
重写是子类对父类的允许访问的方法的实现过程进行重新编写,返回值和形参都不能改变,即外壳不变,核心重写。
重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说子类能够根据需要实现父类的方法。
重写方法不能抛出新的检查异常或者被重写方法申明更加宽泛的异常。例如:父类的方法申明了一个检查异常IOException,但是重写这个方法的时候不能抛出Exception异常,因为Exception是IOException的父类,只能抛出IOException的子类。

方法的重写规则

  • 参数列表必须完全与被重写方法的相同。
  • 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
  • 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
  • 父类的成员方法只能被它的子类重写。
  • 声明为 final 的方法不能被重写。
  • 声明为 static 的方法不能被重写,但是能够被再次声明。
  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
  • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
  • 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
  • 构造方法不能被重写。
  • 如果不能继承一个方法,则不能重写这个方法。

super关键字的使用
当需要在子类中调用父类的被重写方法时,要使用super关键字

重载(Overload)
重载是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。

重载规则:

  • 被重载的方法必须改变参数列表(参数个数或类型不一样);
  • 被重载的方法可以改变返回类型;
  • 被重载的方法可以改变访问修饰符;
  • 被重载的方法可以声明新的或更广的检查异常;
  • 方法能够在同一个类中或者在一个子类中被重载。
  • 无法以返回值类型作为重载函数的区分标准。

重写与重载的区别

区别点 重载方法 重写方法
参数列表 必须修改 一定不能修改
返回类型 可以修改 一定不能修改
异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常
访问 可以修改 一定不能做更严格的限制(可以降低限制)

方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。

(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
(3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。

Java多态

多态是同一个行为具有多个不同表现形式或形态的能力
多态就是同一个接口,使用不同的实例而执行不同操作。

多态的优点
1.消除类型之间的耦合关系
2.可替换性
3.可扩充性
4.接口性
5.灵活性
6.简化性

多态存在的三个必要条件
继承 重写 父类引用指向子类对象。

public class Test {
    public static void main(String[] args) {
      show(new Cat());  // 以 Cat 对象调用 show 方法
      show(new Dog());  // 以 Dog 对象调用 show 方法
                
      Animal a = new Cat();  // 向上转型  
      a.eat();               // 调用的是 Cat 的 eat
      Cat c = (Cat)a;        // 向下转型  
      c.work();        // 调用的是 Cat 的 work
  }  
            
    public static void show(Animal a)  {
      a.eat();  
        // 类型判断
        if (a instanceof Cat)  {  // 猫做的事情 
            Cat c = (Cat)a;  
            c.work();  
        } else if (a instanceof Dog) { // 狗做的事情 
            Dog c = (Dog)a;  
            c.work();  
        }  
    }  
}
 
abstract class Animal {  
    abstract void eat();  
}  
  
class Cat extends Animal {  
    public void eat() {  
        System.out.println("吃鱼");  
    }  
    public void work() {  
        System.out.println("抓老鼠");  
    }  
}  
  
class Dog extends Animal {  
    public void eat() {  
        System.out.println("吃骨头");  
    }  
    public void work() {  
        System.out.println("看家");  
    }  
}

执行输出结果:

吃鱼
抓老鼠
吃骨头
看家
吃鱼
抓老鼠

虚函数
虚函数的存在是为了多态
Java中其实没有虚函数的概念,它的普通函数就相当于C++的虚函数,动态绑定是Java的默认行为。如果Java中不希望某个函数具有虚函数特性,可以加上final关键字变成非虚函数。其实Java和C++在虚函数上的观点大同小异,理念都是类似的。

重写
在Java中,当设计类时,被重写的方法的行为怎样影响多态性。
当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。
要想调用父类中被重写的方法,必须使用关键字super。

多态的实现方式
方式一:重写
方式二:接口
1.生活中的接口最具代表性的就是插座,例如一个三接头的插头都能接在三孔插座中,因为这是每个国家都有自己的接口规则,有可能国外就不行,那是因为国外自己定义的接口类型。
2.Java中的接口类似于生活中的接口,就是一些方法特征的集合,但没有方法的实现。

关于多态,可以总结以下几点:
一、使用父类类型的引用指向子类的对象。
二、该引用只能调用父类中定义的方法和变量
三、如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法,(动态连接,动态调用)
四、变量不能被重写(覆盖),重写的概念只针对方法,如果在子类中重写了父类中的变量,那么在编译时就会报错。

多态的分类
多态一般分为两种,重写式多态和重载式多态。
重载式多态,也叫编译时多态。也就是这种多态在编译时就已经确定好了。重载,方法相同而参数列表不同。在调用这种重载的方法时,通过传入不同的参数得到不同的结果。

重写式多态,也叫运行时多态。这种多态通过动态绑定技术来实现,是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。也就是说,只有程序运行起来才知道调用的是哪个子类的方法。这种多态通过函数的重写以及向上转型来实现。

Vue路由守卫

路由钩子函数

全局的路由钩子函数
to:目标路由
from:前一个路由
next:必须执行next才能跳转路由,执行next()就会跳到目标路由,执行next(false)就不跳转,会停在当前路由。执行next(xxx),就会跳到xxx页面

router.beforeEach( (to,from,next)=> { next() } ) 跳转前的函数

router.afterEach( (to,from)=> { } ) 跳转后的函数

单独路由守卫:
写进某个页面的路由配置里。如:
beforeEnter((to,from,next)=> { console.log('即将进入这个页面'); next() } )

单个路由守卫可以直接写到vue页面(实例)里
beforeRouterEnter(){to,from,next)=> { console.log('即将进入这个页面'); next() }
即使是在next之后,也无法读取到this。但是在next里写个回调函数就可以拿到this了

先执行全局的beforeEach再执行自身的beforeEnter,然后是组件的beforeRouterEnter,然后是afterEach

beforeRouterUpdate((to,from,next)=> { console.log('即将进入这个页面'); next() } )
这个守卫可以读取到this

Vue render函数

render函数和template一样都是创建html模板的,但某些情况下使用template会出现大量的冗余数据。此时用render可以解决这个问题。

render函数即渲染函数,它是个函数,它的参数也是个函数,即createElement

render函数的返回值
为VNode,即虚拟节点,是需要渲染的节点。

render函数的参数
createElement是render的参数。createElement作为一个函数,它的返回值也是VNode,它有三个参数。

createElement参数

  1. 一个 HTML 标签字符串,组件选项对象,或者解析上述任何一种的一个 async 异步函数。类型:{String | Object | Function}。必需。
  2. 一个包含模板相关属性的数据对象你可以在 template 中使用这些特性。类型:{Object}。可选。
  3. 子虚拟节点 (VNodes),由 createElement() 构建而成,也可以使用字符串来生成“文本虚拟节点”。类型:{String | Array}。可选。

这是render的官方指南

以下代码是实际项目中的代码,它是放在iview的table组件中的columns数据里的,用来给table中每一行添加操作按钮,包括编辑删除和查看。

        {
          title: '操作',
          align: 'center',
          width: 300,
          render: (h, { row }) => {
            return h(
              'div',
              {
                class: 'actions'
              },
              [
                h(
                  Button,
                  {
                    class: 'special',
                    attrs: {
                      size: 'small',
                      type: 'primary'
                    },
                    on: {
                      click: () => {
                        this.showEdit(row)
                      }
                    }
                  },
                  '编辑'
                ),
                h(
                  Button,
                  {
                    class: 'special',
                    attrs: {
                      size: 'small',
                      type: 'error'
                    },
                    on: {
                      click: () => {
                        this.deleteItem(row)
                      }
                    }
                  },
                  '删除'
                ),
                h(
                  Button,
                  {
                    class: 'special',
                    attrs: {
                      size: 'small',
                      type: 'success'
                    },
                    on: {
                      click: () => {
                        this.showDetail(row)
                      }
                    }
                  },
                  '查看'
                )
              ]
            )
          }
        }

webpack配置

js语法检查 eslint
需要eslint和eslint-loader
eslint只检查自己写的源代码,第三方库不检查
在package.json里的eslintConfig中设置eslint使用哪种风格

rules:[
{
test: /.js$/,
exclude: /node_modules/, //表示不检查node_modules 里的库
loader: 'eslint-loader',
options: {
fix: true //自动修复
}
}
]

js兼容性处理:
需要下载babel-loader 和 @babel/preset-env,@babel/core
会把es6以上的语法转换成es5等兼容性代码
babel-loader:

test : /.js$/,
exclude : /node_modules/, //排除第三方库
options: {
// 指示babel做怎样的兼容性处理
presets: ['@babel/preset-env']
}

但是只能转换基本语法,像promise就不能转换。
解决方案一:@babel/polyfill 这不是插件,只需要在js文件里引入就行了
import '@babel/polyfill';
问题:体积太大了

解决方案二:
按需加载。下载core-js

test : /.js$/,
exclude : /node_modules/,
loader: 'babel-loader',
options : {
presets:[
[
'@babel/preset-env',
{
useBuildIns: 'usage', //按需加载
corejs: {
version: 3 //版本
},
targets: {
//指定兼容性做到哪个浏览器
chrome:’60‘,
ie: ’9‘
}
}
]
]

}

webpack配置

生产环境下会自动压缩js代码。
压缩html:
html不需要兼容性处理,只需要进行压缩

使用html-webpack-plugin

const HtmlWebpackPlugin = require('html-webpack-plugin')

plugins:[
new HtmlWebpackPlugin({
template: 'src/index.html',
minify: { collapseWhitespace: true, removeComments: true } //移除空格和注释
})
]

在执行多个loader的时候一定要指定顺序,先用eslint后用babel。一定是先风格化代码然后再进行兼容性处理。否则babel转换后在eslint里就会报语法错误。

可以在eslint-loader 上写enforce:’pre‘,表示优先执行

4.闭包

定义
闭包就是能够读取其他函数内部变量的函数。
「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。
闭包是JS作用域的一个副产品,说到底是执行上下文和词法作用域的问题。

闭包的特点

  1. 让外部访问函数内部变量成为可能;
  2. 局部变量会常驻在内存中;
  3. 可以避免使用全局变量,防止全局变量污染;
  4. 会造成内存泄漏(有一块内存空间被长期占用,而不被释放),这是IE的bug

闭包的作用
闭包可以间接访问一个变量,隐藏数据,即保护函数内数据不被外界干扰。
闭包可以延长函数内变量的生命周期。

function fn1(){
  var a = 0;
  function fn2(){
    a++;
    console.log(a);
    }
  return fn2;
}
var f = fn1();
f()  //1
f()  //2
在这里,闭包延长了a的存在时间并保存了状态。一般函数执行完后就会被回收,函数内部的变量也都会死亡,但是由于闭包的存在,使得函数结束后内部的变量也能存在。并且通过闭包可以访问函数内部变量。

产生闭包的条件:
当一个嵌套的内部函数使用了外部函数的变量时产生闭包。这时调用几次外部函数,就产生了几次闭包。

闭包的生命周期:
产生:在嵌套内部函数_定义_执行完时就产生了闭包(不是调用)
死亡:在嵌套的内部函数成为垃圾对象。(例如把内部函数赋值为null)

form-create

form-create是一个表单生成器

在main.js中引入form-create:

import Vue from 'vue';
import iView from 'iview';
import 'iview/dist/styles/iview.css';

//iviewUI
import formCreate from 'form-create'
//获取生成器
import { maker } from 'form-create'

//ElementUI
//import formCreate from 'form-create/element'
//获取生成器
//import { maker } from 'form-create/element'

//三级联动数据,不用可以不引入
import 'form-create/district/province_city_area.js'

Vue.use(iView);
Vue.use(formCreate)

有三种方式创建表单:
组件模式,Vue原型方法,Window全局方法

Cesium 3D Tiles

3D Tiles是流式传输大规模异构3D地理空间数据集的开放规范。Cesium支持的3D内容包括建筑物数据集,CAD或BIM模型,点云和摄影测量模型。

在Cesium中加载3D Tiles:
let tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({url : url}));

new Cesium.Cesium3DTileset(option)

Cesium3DTileset配置

常用的几个属性:
classificationType。用于倾斜摄影单体化设置
readyPromise。用于指定当模型加载完毕后进行的操作,是个Promise对象。获取将在加载tileset的根图块并且tileset准备好呈现时解决的承诺。这一承诺是在第一帧之前的帧结束时解决的

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.