本系列内容主要介绍webgis开发过程中可能会遇到的常见面试题和答案,从前端到二维到三维,干货满满。记得关注我不走丢!
需要更多面试题、视频讲解、webgis教程的宝子戳↓↓↓
前几期内容点击下方链接:
1、TypeScript
TypeScript是微软开发的⼀门编程语⾔,它最终还是会编译成JavaScript,兼容JavaScript所有的语法。
在ts中变量的类型很重,值不是那么重要,ts中的type已经描述了这⻔编程语⾔。
1、type⾃定义数据类型
2、interface
3、namespace
4、enum,void,泛型,联合类型
5、类型声明文件
全局直接使用,不同导入
export {}
declare global {
/* 这⾥⾯的变量默认全部可以直接获取 */
type Person ={
name:string
}
}
2、$.get和$.post的区别
在jQuery中,$.get和$.post都是用于向服务器发送异步请求的方法。它们的主要区别在于它们发送请求 的⽅式和处理响应的⽅式。下面是它们的详细区别:
1、请求方式
$.get使用HTTP GET方法向服务器发送请求,$.post使用HTTP POST方法向服务器发送请求。
2、参数传递方式
$.get方法将请求参数附加到URL后面,⽽$.post方法将请求参数附加到HTTP请求正文中。
3、数据类型
$.get方法默认将服务器响应的数据作为纯文本返回,⽽$.post方法默认将服务器响应的数据作为HTML 或XML文档返回。
4、异常处理
$.get方法通过回调函数来处理服务器响应的数据,如果请求失败,则回调函数将不会被执⾏。$.post方法也通过回调函数来处理服务器响应的数据,但如果请求失败,它可以通过错误处理回调函数来处理异 常情况。
5、性能
由于$.get方法使用GET请求,因此每次发送请求时,请求数据都会被附加到URL中。这可能会导致数据 泄露和对URL⻓度的限制。$.post方法将请求数据附加到请求正文中,从⽽避免了这些问题。
总体来说,如果你需要向服务器提交⼀些数据,你应该使用$.post方法。如果你需要从服务器获取⼀些 数据,$.get方法是更合适的选择。当然,如果你在处理⼤量数据时,$.post方法可能会更快,因为它使用了更有效的传输方式。
3、判断⼀个字符串中出现次数最多的字符,统 计这个次数
在JavaScript中,可以使用对象来统计字符串中每个字符出现的次数,然后再遍历对象找到出现次 数最多的字符。以下是实现这个过程的示例代码:
function findMostFrequentChar(str) {
let charCounts = {};
// 统计字符出现次数
for (let i = 0; i < str.length; i++) {
let char = str[i];
if (charCounts[char]) {
charCounts[char]++;
} else {
charCounts[char] = 1;
}
}
let maxChar = '';
let maxCount = 0;
// 遍历对象找出出现次数最多的字符和次数
for (let char in charCounts) {
if (charCounts[char] > maxCount) {
maxChar = char;
maxCount = charCounts[char];
}
}
return [maxChar, maxCount];
}
// 测试
let str = 'hello world';
let [char, count] = findMostFrequentChar(str);
console.log(`出现次数最多的字符是 ${char},出现了 ${count} 次`);
4、ES6新特性
ES6(ECMAScript 2015)是JavaScript的⼀个重要版本,引入了许多新特性和语法,以提⾼ JavaScript的开发效率和代码质量。以下是⼀些ES6的新特性:
1. let 和const 声明变量:let 和const 是新的变量声明方式,可以替代原来的var。let 声明 的变量作用域是块级作用域,⽽const 声明的变量是常量,不可修改。
2. 箭头函数:箭头函数是⼀种新的函数声明方式,使用箭头符号=> 来定义。它们简化了函数声 明的语法,同时也让代码更加简洁。
3. 模板字符串:模板字符串是⼀种新的字符串表示方式,它可以包含变量,并使用反引号() 来定义。在模板字符串中,可以使用${} 来插入变量值。
4. 解构赋值:解构赋值是⼀种方便的赋值方式,可以从数组或对象中提取值,并将其赋给变量。 5. 对象字面量扩展:对象字面量扩展使得创建对象变得更加容易和直观。ES6 中可以使用新的
语法来定义对象字面量,包括简写属性名、计算属性名、方法定义等。
6. 类:ES6 引入了类的概念,让JavaScript 更加面向对象。类是⼀种模板,用于创建对象。 ES6 中的类定义可以继承⾃其他类,并且支持构造函数和方法定义。
7. 模块化:ES6 引入了模块化的概念,让JavaScript 更加易于组织和管理代码。模块是⼀个独 ⽴的文件,包含⼀组相关的函数、变量和类。在模块中,可以使用export 和import 关键字 来导出和导入模块的内容。
这些是ES6 中⼀些重要的新特性,除此之外,还有更多的新特性,⽐如迭代器、⽣成器、 Promise、async/await 等。这些新特性⼤⼤提⾼了JavaScript 的表现力和代码质量。
5、flexible.js阿里
https://github.com/amfe/lib-flexible 不管屏幕⼤⼩都识别为10rem
在Vite中如何使用
1、安装依赖
cnpm i amfe-flexible postcss-pxtorem postcss -D
2、配置vite.config.js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import px2rem from 'postcss-pxtorem'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
css: {
postcss: {
plugins: [
px2rem({
rootValue: 75, // 1rem的⼤⼩
propList: ['*'], // 需要转换的属性,这⾥选择全部都进⾏转换 unitPrecision: 10
})
]
},
}
})
3、配置main.js
import 'amfe-flexible';
6、深拷贝和浅拷贝
简单数组
var arr = [1,2,3,4,5]; //原数组
var newArr1 = arr; //浅拷⻉
var newArr2 = arr.slice(); //深拷⻉
var newArr3 = arr.concat(); //深拷⻉
var newArr4 = [...arr]; //深拷⻉
var newArr5 = JSON.parse(JSON.stringify(arr)); //深拷⻉ arr[0] = 'leon';
arr[1]= '27';
console.log(arr);
console.log(newArr1);
console.log(newArr2);
console.log(newArr3);
console.log(newArr4);
console.log(newArr5);
复杂数组
只能用JSON.parse.(JSON.stringify(arr))
var arr = [{name: 'wens'},{age: '26'}]; //原数组
var newArr1 = arr; //浅拷⻉
var newArr2 = arr.slice(); //浅拷⻉
var newArr3 = arr.concat(); //浅拷⻉
var newArr4 = [...arr]; //浅拷⻉
var newArr5 = JSON.parse(JSON.stringify(arr)); //深拷⻉ arr[0].name = 'leon';
arr[1].age = '27';
console.log(arr);
console.log(newArr1);
console.log(newArr2);
console.log(newArr3);
console.log(newArr4);
console.log(newArr5);
简单对象
var obj = {
"name": 'xu',
"age": 23
} //原对象
var newObj1 = obj; //浅拷⻉
var newObj2 = Object.assign({},obj); //深拷⻉
var newObj3 = {...obj}; //深拷⻉
var newObj4 = JSON.parse(JSON.stringify(obj)); //深拷⻉ obj.name = 'leon';
obj.age = 666 ;
console.log(obj);
console.log(newObj1);
console.log(newObj2);
console.log(newObj3);
console.log(newObj4);
复杂对象
var obj = {
name: 'xu',
age: 23,
num:[1,2,3,4]
} //原对象
var newObj1 = obj; //浅拷⻉
var newObj2 = Object.assign({},obj); //浅拷⻉
var newObj3 = {...obj}; //浅拷⻉
var newObj4 = JSON.parse(JSON.stringify(obj)); //深拷⻉ obj.name = 'leon';
obj.num[0] = 6;
console.log(obj);
console.log(newObj1);
console.log(newObj2);
console.log(newObj3);
console.log(newObj4);
算法实现深拷贝
var arr = [{name:"cheng",age:18},{name:"zhang",age:19}];
var b = [...arr]; //当是数组嵌套对象的时候,实现的是⼀个浅拷⻉
b.push({
name:"vue",age:15
})
b[0].name = "wang"
console.log(b);
console.log(arr);
var arr = [{name:"cheng",age:18},{name:"zhang",age:19}]; //深克隆算法
function deepClone(obj){
let result ;
if(Array.isArray(obj) || (typeof obj == "object") ){
if(obj instanceof Array){
result= [];
}else{
result = {};
}
for(let key in obj){
result[key] = deepClone(obj[key]); }
}else{
return obj;
}
return result;
}
let res = deepClone(arr);
7、axios的请求如何取消
Axios 是⼀个基于Promise 的HTTP 请求客户端,提供了许多方便的功能,包括请求的取消。取消请求 是在应用程序中很常见的场景,例如用户想要取消⼀个⻓时间的请求或在用户离开⻚面之前取消⼀个请 求等。
Axios 提供了⼀个CancelToken API,可以通过它来取消请求。具体实现方式如下:
1. ⾸先需要创建⼀个CancelToken 实例:
const source = axios.CancelToken.source();
2. 将CancelToken 实例传递给请求的cancelToken 配置项:
axios.get('/api/user', {
cancelToken: source.token
})
3. 当需要取消请求时,调用 CancelToken 实例的cancel()方法:
source.cancel('请求被取消');
完整的示例代码如下:
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/api/user', {
cancelToken: source.token
}).then((response) => {
console.log(response.data);
}).catch((error) => {
if (axios.isCancel(error)) {
console.log('请求被取消', error.message); } else {
console.log(error.message);
}
});
// 取消请求
source.cancel('请求被取消');
在上面的代码中,当请求被取消时,控制台将会输出 "请求被取消"。如果请求没有被取消,则会输出请 求返回的数据。注意,如果请求已经被发送并且正在等待响应,那么取消请求将不会有任何作用。
8、Git工作流
Git是⼀种分布式版本控制系统,它可以支持多种工作流,以下是其中两种⽐较常见的工作流:
1. 集中式工作流
在集中式工作流中,所有的开发⼈员都向同⼀个中央代码库提交代码。通常情况下,这个中央代码库是 由⼀个团队负责维护的,其他团队成员向这个中央代码库提交代码,并从中央代码库中拉取最新的代 码。
该工作流的优点是⽐较简单,适用于⼩型团队,但缺点是中央代码库的故障可能会影响整个团队的工作。
2. 分支式工作流
在分支式工作流中,每个开发⼈员都有⾃⼰的分支,他们可以在自己的分支上进行开发,并在开发完成后将代码合并到主分支(通常是master分支)。如果需要协作开发,可以创建⼀个共享分⽀,多个开发人员可以在这个共享分支上进行开发,并在开发完成后将代码合并到主分支上。
该工作流的优点是比较灵活,多⼈协作时不会相互影响,但需要⽐较严格的代码审核机制,以防止不良代码被合并到主分支上。
无论采用哪种工作流,以下是⼀些常用的Git命令:
git clone:将远程代码库克隆到本地;
git add:将修改后的文件添加到暂存区;
git commit:将暂存区的文件提交到本地代码库;
git push:将本地代码库的代码推送到远程代码库; git pull:将远程代码库的代码拉取到本地;
git branch:查看分支;
git checkout:切换分支;
git merge:合并分支;
git rebase:将⼀个分支的修改放到另⼀个分支的前面。
这些命令是Git的基本命令,熟练掌握这些命令对于使用Git进行代码管理非常重要。
9、JS排序算法
1. 冒泡排序:
它是最基本的排序算法之⼀,每次比较相邻的两个元素,如果它们的顺序不对,就交换 它们。这样⼀轮比较下来,最大的元素就会被排到最后面。然后再对剩下的元素进行相同的比较, 直到所有元素都被排序。
冒泡排序的时间复杂度为 O(n^2)。
function bubbleSort(arr) {
var len = arr.length;
for (var i = 0; i < len - 1; i++) {
for (var j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
2. 插入排序:
它的思路是将数组分为已排序和未排序两个部分,每次从未排序的部分中取出⼀个元 素,插入到已排序的部分中,直到所有元素都被排序。插入排序的时间复杂度也为 O(n^2)。
function insertionSort(arr) {
var len = arr.length;
for (var i = 1; i < len; i++) {
var temp = arr[i];
var j = i - 1;
while (j >= 0 && arr[j] > temp) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = temp;
}
return arr;
}
3. 快速排序:
它是⼀种分治的排序算法,通过选择⼀个基准元素,将数组分为左右两部分,左边的元 素都小于基准元素,右边的元素都大于基准元素,然后递归地对左右两部分进行排序。快速排序的时间复杂度为O(nlogn)。
function quickSort(arr) {
if (arr.length <= 1) {
return arr;
}
var pivotIndex = Math.floor(arr.length / 2);
var pivot = arr.splice(pivotIndex, 1)[0];
var left = [];
var right = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quickSort(left).concat([pivot], quickSort(right));
}
这⾥只是简单介绍了几个常见的排序算法,还有其他的排序算法,如归并排序、堆排序等,读者可以自行了解。
10、函数内部this
1、函数中this指向
函数正常调⽤的时候this指向: window
定时器: window
构造函数:实例化对象
var name = "张三"
function show(){
console.log(this.name);
}
function go(){
var name = "李四"
show()
}
go() // "张三" // window.go() function go(){
var name = "李四"
console.log(this.name); // window.name }
go() // "张三" window.go()
2、改变函数中this指向
2-1 call
funName.call(obj,arg1,arg2...)
var name="王五"
var obj = { name:"李四" }
function go(){
console.log(this.name); }
go() // 王五
go.call(obj) // 李四
2-2 apply
funName.call(obj,[arg1,arg2...])
call,apply的区别:
应⽤场景:传递多个参数的情况
call依次去传递
apply 需要传递数组
var name="window"
var zhang={
name:"张三"
}
function show(a,b){
console.log(this.name);
console.log(a+b);
}
show(1,2)
show.call(zhang,2,3)
show.apply(zhang,[1,2])
2-3 bind
bind 绑定的函数不会⻢上执行,只是改变了函数上下文的执行环境
call,apply调用的时候⻢上执行
var name = "window"
var zhang = {
name:"张三"
}
var show = function (a,b){
console.log(this.name);
console.log(a+b);
}.bind(zhang)
show(2,3) // zhang 5
使用场景
# 解决定时器中this的指向问题
<button id="btn">按钮</button>
<script>
var id="1001";
var btn = document.getElementById("btn")
btn.onclick = function(){
setTimeout(function(){ // window.setTimeout
console.log(this.id); // 1001
},1000)
}
</script>
## 解决
# 1.使⽤bind解决
btn.onclick = function(){
setTimeout(function(){
console.log(this.id);
}.bind(btn),1000)
}
/* btn.onclick = function(){
var self = this
setTimeout(function(){
console.log(this.id);
}.bind(self),1000)
}
btn.onclick = function(){
setTimeout(function(){
console.log(this.id);
}.bind(this),1000)
}
*/
# 2.使⽤箭头函数解决
btn.onclick = function(){
setTimeout(()=>{
console.log(this.id);
},1000)
}
3、React
⼀、事件 onClick
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
msg: "hello react"
};
}
render() {
return (
<div>
<button onClick={this.handleClick}>{this.state.msg}</button>
</div>)
}
handleClick() {
console.log("event")
}
}
⼆、事件中的this丢失
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
msg: "hello react"
};
}
render() {
return (
<div>
<button onClick={this.handleClick}>{this.state.msg}</button>
</div>)
}
handleClick() {
console.log(this) //undefined ❌
}
}
通过App实例对象调用的方法,方法中的this才是App实例对象
在以上代码中,handleClick是作为onClick的回调执行的,不是作为实例的方法调用的,是直接调用的。